Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding OWASP Code #1

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions owasp-top10/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
MONGODB_URI=mongodb://localhost:27017

1 change: 1 addition & 0 deletions owasp-top10/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
venv/
121 changes: 121 additions & 0 deletions owasp-top10/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Educational Vulnerable Application

**WARNING: This application is intentionally vulnerable and meant for educational purposes only. DO NOT deploy this in any production environment.**

## Overview
This application demonstrates common security vulnerabilities based on OWASP Top 10 (2021). It consists of two microservices:
- Auth Service: Handles user authentication with intentional vulnerabilities
- Profile Service: Manages user profile data with intentional vulnerabilities

## Intentional Vulnerabilities

### 1. Broken Access Control (A01:2021)
- No role-based access control implementation
- Direct object references without verification
- Location: `auth_service/routes.py` - endpoint `/api/user/<id>`

### 2. Cryptographic Failures (A02:2021)
- Passwords stored with weak hashing (MD5)
- Sensitive data transmitted without encryption
- Location: `auth_service/utils.py` - `hash_password()` function

### 3. Injection (A03:2021)
- SQL injection vulnerability in login query
- NoSQL injection in profile lookup
- Location: `auth_service/routes.py` - `/login` endpoint
- Location: `profile_service/routes.py` - `/profile` endpoint

### 4. Insecure Design (A04:2021)
- No rate limiting on login attempts
- Password reset without verification
- Location: `auth_service/routes.py` - all endpoints

### 5. Security Misconfiguration (A05:2021)
- Debug mode enabled
- Default/weak credentials
- Location: `config.py` - all configuration settings

### 6. Vulnerable Components (A06:2021)
- Outdated dependencies in requirements.txt
- Known vulnerable versions of packages

### 7. Authentication Failures (A07:2021)
- Weak password requirements
- Session tokens without expiry
- Location: `auth_service/utils.py` - `validate_password()` function

### 8. Software and Data Integrity Failures (A08:2021)
- No integrity checks on uploaded files
- Unsecured deserialization
- Location: `profile_service/routes.py` - `/upload` endpoint

### 9. Security Logging Failures (A09:2021)
- No logging of security events
- Sensitive data in logs
- Location: Both services lack proper logging

### 10. Server-Side Request Forgery (A10:2021)
- Unvalidated URL inputs
- Location: `profile_service/routes.py` - `/fetch-avatar` endpoint

## Setup Instructions

1. Create virtual environment:
```bash
python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
```

2. Install dependencies:
```bash
pip install -r requirements.txt
```

3. Set up MongoDB:
- Use local MongoDB instance or
- Create free MongoDB Atlas cluster

4. Configure environment:
```bash
cp .env.example .env
# Edit .env with your MongoDB URI
```

5. Run services:
```bash
# Terminal 1
python auth_service/app.py

# Terminal 2
python profile_service/app.py
```

Comment on lines +61 to +93
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance setup instructions with security considerations

While the setup instructions are functional, consider adding:

  1. Development environment security recommendations
  2. Specific version requirements
  3. MongoDB security configuration guidelines
 ## Setup Instructions
+
+### Prerequisites
+- Python 3.8 or higher
+- MongoDB 4.4 or higher
+- Virtual environment tool
+
+### Security Considerations
+> ⚠️ Development Environment Safety:
+> - Use a dedicated development environment
+> - Never use production credentials
+> - Isolate the vulnerable application network
 
 1. Create virtual environment:
 ```bash
 python -m venv venv
 source venv/bin/activate  # Linux/Mac
 venv\Scripts\activate     # Windows
  1. Install dependencies:
pip install -r requirements.txt
  1. Set up MongoDB:
    • Use local MongoDB instance or
    • Create free MongoDB Atlas cluster
    • Use local MongoDB instance:
  • ```bash
    
  • # Secure local setup
    
  • mongod --bind_ip 127.0.0.1 --port 27017
    
  • ```
    
    • Or create free MongoDB Atlas cluster:
  • - Enable IP whitelist
    
  • - Create dedicated user with minimal privileges
    
  • - Use strong passwords
    

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 LanguageTool</summary>

[uncategorized] ~63-~63: You might be missing the article “a” here.
Context: ...point  ## Setup Instructions  1. Create virtual environment: ```bash python -m venv ven...

(AI_EN_LECTOR_MISSING_DETERMINER_A)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

## Testing Vulnerabilities

1. SQL Injection:
```
Username: admin' OR '1'='1
Password: anything
```

2. NoSQL Injection:
```javascript
{"$gt": ""} in username field
```

3. Weak Passwords:
```
Any password with length > 1 is accepted
```

4. SSRF Test:
```
/fetch-avatar?url=file:///etc/passwd
```

Comment on lines +94 to +116
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance testing instructions with safety measures and expected outcomes

The testing instructions should include:

  1. Expected outcomes for each test
  2. Safety precautions
  3. Cleanup procedures after testing
 ## Testing Vulnerabilities
+
+> ⚠️ SAFETY FIRST:
+> - Use only in isolated development environment
+> - Never test these payloads against real systems
+> - Clean up test data after experiments
 
 1. SQL Injection:
-```
+```sql
 Username: admin' OR '1'='1
 Password: anything

+Expected outcome: Unauthorized access to admin account
+Cleanup: Reset admin password after testing

  1. NoSQL Injection:
{"$gt": ""} in username field

+Expected outcome: Bypass authentication
+Cleanup: Check and remove any unauthorized sessions

  1. Weak Passwords:
    - +text
    Any password with length > 1 is accepted
+Expected outcome: Creation of account with weak password
+Cleanup: Remove test accounts

4. SSRF Test:
-```
+```http
/fetch-avatar?url=file:///etc/passwd

+Expected outcome: Unauthorized file read
+Cleanup: Review and remove any cached files


<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Markdownlint (0.35.0)</summary>

97-97: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

108-108: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

113-113: null
Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

## Automated Testing
Run security scanners against http://localhost:5000 and http://localhost:5001 to detect vulnerabilities.

## Disclaimer
This application is for educational purposes only. It contains intentional security vulnerabilities to demonstrate common security issues. DO NOT use any of this code in production environments.
9 changes: 9 additions & 0 deletions owasp-top10/auth_service/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Dockerfile for auth-service
FROM node:14

WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install

COPY . .
CMD ["node", "server.js"]
Binary file not shown.
91 changes: 91 additions & 0 deletions owasp-top10/auth_service/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
from flask import Flask, request, jsonify
from flask_cors import CORS # Import CORS
from utils import hash_password, generate_token
import sqlite3
import json

app = Flask(__name__)

# Enable CORS for all routes
CORS(app)

app.config['DEBUG'] = True
app.secret_key = 'xuysoe54Puj990'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid hardcoding secret keys; use environment variables instead

Hardcoding secret keys in the source code can lead to security vulnerabilities if the code is exposed. It's safer to load the secret key from an environment variable or a configuration file not checked into version control.

Apply this change to use an environment variable for the secret key:

-import json
+import os

 app = Flask(__name__)

 # Enable CORS for all routes
 CORS(app)

 app.config['DEBUG'] = True
-app.secret_key = 'xuysoe54Puj990' 
+app.secret_key = os.environ.get('SECRET_KEY', 'your-default-secret-key')

Ensure that the SECRET_KEY environment variable is securely set in your deployment environment.

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Gitleaks (8.21.2)

13-13: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


@app.route('/', methods=['GET'])
def entry():
return jsonify({"error": "Invalid credentials"}), 401

@app.route('/login', methods=['POST'])
def login():
data = request.get_json()
username = data.get('username')
password = data.get('password')

# Verify user credentials (hash password comparison)
query = f"SELECT * FROM users WHERE username='{username}' AND password='{hash_password(password)}'"
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
user = cursor.execute(query).fetchone()
Comment on lines +26 to +29
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Prevent SQL Injection by using parameterized queries in login

The login query concatenates user input directly into the SQL statement, making the application vulnerable to SQL injection attacks. Using parameterized queries protects against this vulnerability.

Apply this diff to use parameterized queries:

-    query = f"SELECT * FROM users WHERE username='{username}' AND password='{hash_password(password)}'"
     conn = sqlite3.connect('users.db')
     cursor = conn.cursor()
-    user = cursor.execute(query).fetchone()
+    cursor.execute(
+        "SELECT * FROM users WHERE username=? AND password=?",
+        (username, hash_password(password))
+    )
+    user = cursor.fetchone()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
query = f"SELECT * FROM users WHERE username='{username}' AND password='{hash_password(password)}'"
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
user = cursor.execute(query).fetchone()
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
cursor.execute(
"SELECT * FROM users WHERE username=? AND password=?",
(username, hash_password(password))
)
user = cursor.fetchone()


if user:
# Generate JWT token
token = generate_token(username)

# Decode token to string and return
return jsonify({"token": token.decode('utf-8')}) # Decode bytes to string
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove unnecessary decode when returning JWT token

The generate_token function returns a string, so calling decode('utf-8') is unnecessary and may cause an error.

Apply this diff to fix the return statement:

-        return jsonify({"token": token.decode('utf-8')})  # Decode bytes to string
+        return jsonify({"token": token})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return jsonify({"token": token.decode('utf-8')}) # Decode bytes to string
return jsonify({"token": token})

return jsonify({"error": "Invalid credentials"}), 401


@app.route('/register', methods=['POST'])
def register():
data = request.get_json()

if len(data.get('password', '')) > 1:
hashed_password = hash_password(data['password'])

# Create connection to SQLite database
conn = sqlite3.connect('users.db')
cursor = conn.cursor()

# Ensure the users table is created if it does not exist
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
)
''')

try:
# Use parameterized queries to prevent SQL injection
cursor.execute(
'INSERT INTO users (username, password) VALUES (?, ?)',
(data['username'], hashed_password)
)
conn.commit()
return jsonify({"message": "User registered successfully"})
except sqlite3.IntegrityError:
# Handle unique constraint violation (duplicate username)
return jsonify({"error": "Username already exists"}), 400
finally:
conn.close()
else:
return jsonify({"error": "Invalid password"}), 400

@app.route('/api/user/<id>', methods=['GET'])
def get_user(id):
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
user = cursor.execute(f"SELECT * FROM users WHERE id={id}").fetchone()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Prevent SQL Injection by using parameterized queries in get_user

The get_user function formats the SQL query with user input directly, which can lead to SQL injection. Use parameterized queries to secure the database interaction.

Apply this diff to use parameterized queries:

-    user = cursor.execute(f"SELECT * FROM users WHERE id={id}").fetchone()
+    cursor.execute("SELECT * FROM users WHERE id=?", (id,))
+    user = cursor.fetchone()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
user = cursor.execute(f"SELECT * FROM users WHERE id={id}").fetchone()
cursor.execute("SELECT * FROM users WHERE id=?", (id,))
user = cursor.fetchone()


if user:
return jsonify({
"id": user[0],
"username": user[1],
"password_hash": user[2]
})
return jsonify({"error": "User not found"}), 404

if __name__ == '__main__':
app.run(port=5000, debug=True)
15 changes: 15 additions & 0 deletions owasp-top10/auth_service/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import hashlib
import jwt

def hash_password(password):
return hashlib.md5(password.encode()).hexdigest()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use a stronger password hashing algorithm

The hash_password function uses MD5, which is considered insecure for password hashing due to its vulnerability to collision attacks. It's recommended to use a stronger hashing algorithm like bcrypt or Argon2.

Consider using the werkzeug.security module for secure password hashing:

-import hashlib
+from werkzeug.security import generate_password_hash

-def hash_password(password):
-    return hashlib.md5(password.encode()).hexdigest()
+def hash_password(password):
+    return generate_password_hash(password)

You'll need to install Werkzeug if it's not already included:

pip install Werkzeug

Also, update the verification logic accordingly where passwords are checked.


def generate_token(username):
return jwt.encode(
{'username': username},
'xuysoe54Puj990',
algorithm='HS256'
)
Comment on lines +8 to +12
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid hardcoding secret keys in generate_token function

Hardcoding the secret key within the function is a security risk. Use an environment variable to store the secret key securely.

Apply this change to use an environment variable:

-import jwt
+import jwt
+import os

 def generate_token(username):
     return jwt.encode(
         {'username': username},
-        'xuysoe54Puj990',
+        os.environ.get('SECRET_KEY', 'your-default-secret-key'),
         algorithm='HS256'
     )

Ensure the SECRET_KEY environment variable is set securely in the deployment environment.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return jwt.encode(
{'username': username},
'xuysoe54Puj990',
algorithm='HS256'
)
import jwt
import os
return jwt.encode(
{'username': username},
os.environ.get('SECRET_KEY', 'your-default-secret-key'),
algorithm='HS256'
)


def validate_password(password):
return len(password) > 1
42 changes: 42 additions & 0 deletions owasp-top10/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- REACT_APP_AUTH_SERVICE_URL=http://auth-service:5000
- REACT_APP_PROFILE_SERVICE_URL=http://profile-service:5001
depends_on:
- auth-service
- profile-service
auth-service:
build:
context: ./auth_service
dockerfile: Dockerfile
ports:
- "5000:5000"
environment:
- MONGO_URI=mongodb://mongo:27017/
depends_on:
- mongo
profile-service:
build:
context: ./profile_service
dockerfile: Dockerfile
ports:
- "5001:5001"
environment:
- MONGO_URI=mongodb://mongo:27017/
depends_on:
- mongo
mongo:
image: mongo:4.4
ports:
- "27017:27017"
volumes:
- mongodb_data:/data/db
volumes:
mongodb_data:
11 changes: 11 additions & 0 deletions owasp-top10/frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Dockerfile for frontend
FROM node:14

WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install

COPY . .
RUN npm run build

CMD ["npm", "start"]
Loading