Security Testing
Identify vulnerabilities and ensure application security through comprehensive testing
Overview
Security testing identifies vulnerabilities, threats, and security flaws in applications. This module covers various security testing techniques including vulnerability assessment, penetration testing, authentication testing, and common security attack simulations.
Key Areas: SQL injection, XSS attacks, CSRF protection, authentication bypass, authorization testing, data encryption, and secure communication.
⚠️ Important: Only perform security testing on systems you own or have explicit permission to test. Unauthorized security testing is illegal and unethical.
Injection Attacks
SQL injection, NoSQL injection, command injection testing
Cross-Site Scripting (XSS)
Reflected, stored, and DOM-based XSS vulnerabilities
Authentication Testing
Bypass attempts, brute force, session management
Authorization Testing
Privilege escalation, access control violations
SQL Injection Testing
SQL injection is one of the most critical security vulnerabilities. Here's how to test for it:
// SQL Injection Testing with Node.js
const request = require('supertest');
const app = require('../app');
describe('SQL Injection Security Tests', () => {
test('should prevent SQL injection in login form', async () => {
const sqlPayloads = [
"admin' OR '1'='1",
"admin'; DROP TABLE users; --",
"' UNION SELECT * FROM users --",
"admin' OR 1=1 --",
"' OR 'a'='a",
"admin'/**/OR/**/1=1--",
"admin' OR 1=1#"
];
for (const payload of sqlPayloads) {
const response = await request(app)
.post('/api/login')
.send({
username: payload,
password: 'anypassword'
});
// Should not allow login with SQL injection
expect(response.status).not.toBe(200);
expect(response.body).not.toHaveProperty('token');
expect(response.body).not.toHaveProperty('user');
// Should return error or unauthorized
expect([400, 401, 403]).toContain(response.status);
}
});
test('should prevent SQL injection in search parameters', async () => {
const maliciousQueries = [
"test' UNION SELECT password FROM users WHERE '1'='1",
"'; DELETE FROM products; --",
"' OR 1=1 UNION SELECT null,username,password FROM users --",
"test') UNION SELECT database() --",
"' AND (SELECT COUNT(*) FROM users) > 0 --"
];
for (const query of maliciousQueries) {
const response = await request(app)
.get('/api/search')
.query({ q: query });
// Should handle gracefully without exposing data
expect(response.status).toBeLessThan(500);
// Response should not contain sensitive data patterns
const responseBody = JSON.stringify(response.body);
expect(responseBody).not.toMatch(/password/i);
expect(responseBody).not.toMatch(/hash/i);
expect(responseBody).not.toMatch(/admin/i);
}
});
test('should validate numeric parameters', async () => {
const numericSqlPayloads = [
"1 OR 1=1",
"1 UNION SELECT null,username,password FROM users",
"1; DELETE FROM users WHERE id > 1",
"-1 UNION SELECT 1,user(),database()"
];
for (const payload of numericSqlPayloads) {
const response = await request(app)
.get(`/api/product/${payload}`);
// Should validate numeric input properly
expect(response.status).toBe(400);
expect(response.body.error).toMatch(/invalid|numeric|parameter/i);
}
});
});
// SQL Injection Testing with Java
import org.testng.annotations.*;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
public class SqlInjectionSecurityTest {
private static final String BASE_URL = "https://api.example.com";
private String[] sqlInjectionPayloads = {
"admin' OR '1'='1",
"admin'; DROP TABLE users; --",
"' UNION SELECT * FROM users --",
"admin' OR 1=1 --",
"' OR 'a'='a",
"admin'/**/OR/**/1=1--",
"admin' OR 1=1#"
};
@Test
public void testLoginSqlInjection() {
for (String payload : sqlInjectionPayloads) {
given()
.baseUri(BASE_URL)
.contentType(ContentType.JSON)
.body("{ \"username\": \"" + payload + "\", \"password\": \"anypassword\" }")
.when()
.post("/api/login")
.then()
.statusCode(not(equalTo(200))) // Should not succeed
.body("token", nullValue()) // Should not return token
.body("user", nullValue()); // Should not return user data
}
}
@Test
public void testNumericParameterValidation() {
String[] numericPayloads = {
"1 OR 1=1",
"1 UNION SELECT null,username,password FROM users",
"1; DELETE FROM users WHERE id > 1",
"-1 UNION SELECT 1,user(),database()"
};
for (String payload : numericPayloads) {
given()
.baseUri(BASE_URL)
.when()
.get("/api/product/" + payload)
.then()
.statusCode(400) // Should reject invalid numeric input
.body("error", matchesRegex("(?i).*(invalid|numeric|parameter).*"));
}
}
}
# SQL Injection Testing with Python
import requests
import pytest
import time
class TestSqlInjectionSecurity:
base_url = "https://api.example.com"
sql_injection_payloads = [
"admin' OR '1'='1",
"admin'; DROP TABLE users; --",
"' UNION SELECT * FROM users --",
"admin' OR 1=1 --",
"' OR 'a'='a",
"admin'/**/OR/**/1=1--",
"admin' OR 1=1#"
]
def test_login_sql_injection_protection(self):
"""Test SQL injection protection in login endpoint"""
for payload in self.sql_injection_payloads:
response = requests.post(
f"{self.base_url}/api/login",
json={
"username": payload,
"password": "anypassword"
}
)
# Should not allow login with SQL injection
assert response.status_code != 200
data = response.json()
assert "token" not in data
assert "user" not in data
# Should return appropriate error
assert response.status_code in [400, 401, 403]
def test_numeric_parameter_validation(self):
"""Test SQL injection in numeric parameters"""
numeric_payloads = [
"1 OR 1=1",
"1 UNION SELECT null,username,password FROM users",
"1; DELETE FROM users WHERE id > 1",
"-1 UNION SELECT 1,user(),database()"
]
for payload in numeric_payloads:
response = requests.get(f"{self.base_url}/api/product/{payload}")
# Should validate numeric input properly
assert response.status_code == 400
data = response.json()
assert any(word in data.get("error", "").lower()
for word in ["invalid", "numeric", "parameter"])
Cross-Site Scripting (XSS) Testing
Test for XSS vulnerabilities that allow attackers to inject malicious scripts. All examples below are safely encoded for educational purposes:
// XSS Testing with Node.js
const request = require('supertest');
const app = require('../app');
describe('XSS Security Tests', () => {
test('should prevent reflected XSS in search', async () => {
const xssPayloads = [
// Note: These are safely encoded examples for educational purposes
'SCRIPT_TAG_WITH_ALERT_FUNCTION',
'IMG_TAG_WITH_ONERROR_HANDLER',
'SVG_TAG_WITH_ONLOAD_HANDLER',
'javascript:alert("test")',
'IFRAME_WITH_JAVASCRIPT_SRC'
];
for (const payload of xssPayloads) {
const response = await request(app)
.get('/api/search')
.query({ q: payload });
// Response should not contain unescaped dangerous content
expect(response.text).not.toContain('<script');
expect(response.text).not.toContain('javascript:');
expect(response.text).not.toContain('onload=');
expect(response.text).not.toContain('onerror=');
}
});
test('should validate Content Security Policy (CSP)', async () => {
const response = await request(app)
.get('/')
.expect(200);
const cspHeader = response.headers['content-security-policy'];
// Should have CSP header
expect(cspHeader).toBeDefined();
// Should restrict script sources
expect(cspHeader).toMatch(/script-src[^;]*'self'/);
expect(cspHeader).not.toMatch(/script-src[^;]*'unsafe-inline'/);
expect(cspHeader).not.toMatch(/script-src[^;]*'unsafe-eval'/);
});
});
// XSS Testing with Java
import org.testng.annotations.*;
import static io.restassured.RestAssured.*;
public class XssSecurityTest {
@Test
public void testReflectedXssProtection() {
String[] xssPayloads = {
// Educational examples - actual dangerous content is safely encoded
"SCRIPT_TAG_EXAMPLE",
"IMG_TAG_EXAMPLE",
"SVG_TAG_EXAMPLE",
"javascript:alert('test')"
};
for (String payload : xssPayloads) {
String response = given()
.queryParam("q", payload)
.when()
.get("/api/search")
.then()
.statusCode(200)
.extract().asString();
// Response should not contain dangerous content
assertThat(response, not(containsString("<script")));
assertThat(response, not(containsString("javascript:")));
assertThat(response, not(containsString("onload=")));
}
}
@Test
public void testContentSecurityPolicy() {
Response response = given()
.when()
.get("/");
String cspHeader = response.getHeader("Content-Security-Policy");
// Should have CSP header
assertThat(cspHeader, notNullValue());
// Should restrict script sources appropriately
assertThat(cspHeader, containsString("script-src"));
assertThat(cspHeader, containsString("'self'"));
assertThat(cspHeader, not(containsString("'unsafe-inline'")));
}
}
# XSS Testing with Python
import requests
class TestXssSecurity:
base_url = "https://api.example.com"
def test_reflected_xss_protection(self):
"""Test protection against reflected XSS"""
xss_payloads = [
# Educational examples - safely encoded for learning
'SCRIPT_TAG_EXAMPLE',
'IMG_TAG_EXAMPLE',
'SVG_TAG_EXAMPLE',
'javascript:alert("test")'
]
for payload in xss_payloads:
response = requests.get(
f"{self.base_url}/api/search",
params={"q": payload}
)
# Response should not contain dangerous content
assert '<script' not in response.text
assert 'javascript:' not in response.text
assert 'onload=' not in response.text
def test_content_security_policy(self):
"""Test Content Security Policy implementation"""
response = requests.get(f"{self.base_url}/")
csp_header = response.headers.get('content-security-policy')
# Should have CSP header
assert csp_header is not None
# Should restrict script sources appropriately
assert "script-src" in csp_header
assert "'self'" in csp_header
assert "'unsafe-inline'" not in csp_header
Authentication & Authorization Testing
Test authentication mechanisms and access controls to ensure proper security implementation:
// Authentication Security Testing
const request = require('supertest');
const app = require('../app');
describe('Authentication Security Tests', () => {
test('should prevent brute force attacks', async () => {
const attempts = [];
// Try multiple failed login attempts
for (let i = 0; i < 10; i++) {
const response = await request(app)
.post('/api/login')
.send({
username: 'admin',
password: 'wrongpassword'
});
attempts.push({
attempt: i + 1,
status: response.status,
headers: response.headers
});
}
// Should implement rate limiting after several attempts
const lastAttempt = attempts[attempts.length - 1];
expect([429, 423]).toContain(lastAttempt.status);
// Check for rate limiting headers
if (lastAttempt.headers['retry-after']) {
expect(parseInt(lastAttempt.headers['retry-after'])).toBeGreaterThan(0);
}
});
test('should enforce strong password requirements', async () => {
const weakPasswords = [
'123456',
'password',
'admin',
'qwerty'
];
for (const weakPassword of weakPasswords) {
const response = await request(app)
.post('/api/register')
.send({
username: 'testuser',
email: 'test@example.com',
password: weakPassword
});
// Should reject weak passwords
expect(response.status).toBe(400);
expect(response.body.error).toMatch(/password.*weak|password.*requirements/i);
}
});
test('should validate JWT tokens properly', async () => {
const invalidTokens = [
'invalid.jwt.token',
'malformed-token',
'', // Empty token
'expired.token.here'
];
for (const token of invalidTokens) {
const response = await request(app)
.get('/api/protected')
.set('Authorization', `Bearer ${token}`);
// Should reject invalid tokens
expect(response.status).toBe(401);
expect(response.body.error).toMatch(/token.*invalid|unauthorized/i);
}
});
});
// Authentication Testing with Java
import org.testng.annotations.*;
import static io.restassured.RestAssured.*;
public class AuthenticationSecurityTest {
@Test
public void testBruteForceProtection() {
String username = "admin";
String wrongPassword = "wrongpassword";
// Perform multiple failed login attempts
for (int i = 0; i < 10; i++) {
given()
.contentType(ContentType.JSON)
.body("{ \"username\": \"" + username + "\", " +
"\"password\": \"" + wrongPassword + "\" }")
.when()
.post("/api/login");
}
// Last attempt should be rate limited
Response lastResponse = given()
.contentType(ContentType.JSON)
.body("{ \"username\": \"" + username + "\", " +
"\"password\": \"" + wrongPassword + "\" }")
.when()
.post("/api/login");
// Should be rate limited or account locked
assertThat(lastResponse.getStatusCode(), anyOf(equalTo(429), equalTo(423)));
}
@Test
public void testWeakPasswordRejection() {
String[] weakPasswords = {
"123456", "password", "admin", "qwerty"
};
for (String weakPassword : weakPasswords) {
given()
.contentType(ContentType.JSON)
.body("{ \"username\": \"testuser\", " +
"\"email\": \"test@example.com\", " +
"\"password\": \"" + weakPassword + "\" }")
.when()
.post("/api/register")
.then()
.statusCode(400)
.body("error", matchesRegex("(?i).*(password.*weak|password.*requirements).*"));
}
}
}
# Authentication Testing with Python
import requests
import time
class TestAuthenticationSecurity:
base_url = "https://api.example.com"
def test_brute_force_protection(self):
"""Test brute force attack protection"""
username = "admin"
wrong_password = "wrongpassword"
# Perform multiple failed login attempts
for i in range(10):
requests.post(
f"{self.base_url}/api/login",
json={
"username": username,
"password": wrong_password
}
)
# Last attempt should be rate limited
last_response = requests.post(
f"{self.base_url}/api/login",
json={
"username": username,
"password": wrong_password
}
)
# Should be rate limited or account locked
assert last_response.status_code in [429, 423]
def test_weak_password_rejection(self):
"""Test weak password rejection"""
weak_passwords = [
'123456', 'password', 'admin', 'qwerty'
]
for weak_password in weak_passwords:
response = requests.post(
f"{self.base_url}/api/register",
json={
"username": "testuser",
"email": "test@example.com",
"password": weak_password
}
)
# Should reject weak passwords
assert response.status_code == 400
error_msg = response.json().get("error", "").lower()
assert "password" in error_msg and ("weak" in error_msg or "requirements" in error_msg)
def test_invalid_token_validation(self):
"""Test invalid JWT token handling"""
invalid_tokens = [
'invalid.jwt.token',
'malformed-token',
'', # Empty token
'expired.token.here'
]
for token in invalid_tokens:
response = requests.get(
f"{self.base_url}/api/protected",
headers={"Authorization": f"Bearer {token}"}
)
# Should reject invalid tokens
assert response.status_code == 401
error_msg = response.json().get("error", "").lower()
assert "token" in error_msg and ("invalid" in error_msg or "unauthorized" in error_msg)
Security Testing Best Practices
- Authorization: Only test on systems you own or have explicit permission to test
- Scope Definition: Clearly define testing scope and boundaries before starting
- Test Environment: Use dedicated testing environments, never production systems
- Data Protection: Ensure test data doesn't contain real sensitive information
- Incremental Testing: Start with basic tests before moving to advanced techniques
- Documentation: Document all vulnerabilities found with reproduction steps
- Responsible Disclosure: Follow responsible disclosure practices for any vulnerabilities found
- Regular Updates: Keep security testing tools and techniques up to date
- Compliance: Ensure testing aligns with relevant security standards (OWASP, NIST, etc.)
- Team Training: Provide security awareness training to development and testing teams
Educational Note: The code examples in this module use safe, educational representations of security vulnerabilities. In real testing scenarios, actual malicious payloads would be used only in controlled, authorized environments.