Защита от атак

Ниже приведены практические примеры, как уязвимый код превращается в защищенный. Примеры даны для Node.js (JS/TS), Python, PHP и Go.

XSS (Cross-Site Scripting)

Защита от внедрения скриптов через пользовательский ввод. Основные меры: экранирование вывода, шаблонизаторы с автоэкранированием, CSP.

Уязвимый код
app.get('/search', (req, res) => {
  const q = req.query.q || '';
  // VULN: unescaped HTML
  res.send(`

Search: ${q}

`); });
Защищенный код
const escape = require('escape-html');
app.get('/search', (req, res) => {
  const q = req.query.q || '';
  res.send(`

Search: ${escape(q)}

`); });
Уязвимый код
app.get('/search', (req, res) => {
  const q = String(req.query.q || '');
  // VULN: unescaped HTML
  res.send(`

Search: ${q}

`); });
Защищенный код
import escape from 'escape-html';
app.get('/search', (req, res) => {
  const q = String(req.query.q || '');
  res.send(`

Search: ${escape(q)}

`); });
Уязвимый код
@app.get('/search')
def search():
    q = request.args.get('q', '')
    # VULN: unescaped HTML
    return f"

Search: {q}

"
Защищенный код
from markupsafe import escape

@app.get('/search')
def search():
    q = request.args.get('q', '')
    return f"

Search: {escape(q)}

"
Уязвимый код
$q = $_GET['q'] ?? '';
// VULN: unescaped HTML
echo "

Search: $q

";
Защищенный код
$q = $_GET['q'] ?? '';
echo '

Search: ' . htmlspecialchars($q, ENT_QUOTES, 'UTF-8') . '

';
Уязвимый код
q := r.URL.Query().Get("q")
// VULN: unescaped HTML
fmt.Fprintf(w, "

Search: %s

", q)
Защищенный код
q := r.URL.Query().Get("q")
fmt.Fprintf(w, "

Search: %s

", template.HTMLEscapeString(q))

SQL Injection

Защита от подмены SQL-запросов. Основные меры: параметризованные запросы, ORM, минимальные привилегии БД.

Уязвимый код
const email = req.body.email;
// VULN: string concatenation
db.query(`SELECT * FROM users WHERE email = '${email}'`);
Защищенный код
const email = req.body.email;
await db.execute('SELECT * FROM users WHERE email = ?', [email]);
Уязвимый код
const email = String(req.body.email);
// VULN: string concatenation
await db.query(`SELECT * FROM users WHERE email = '${email}'`);
Защищенный код
const email = String(req.body.email);
await db.execute('SELECT * FROM users WHERE email = ?', [email]);
Уязвимый код
email = request.form['email']
# VULN: string interpolation
cursor.execute(f"SELECT * FROM users WHERE email = '{email}'")
Защищенный код
email = request.form['email']
cursor.execute('SELECT * FROM users WHERE email = %s', (email,))
Уязвимый код
$email = $_POST['email'];
// VULN: concatenation
mysqli_query($db, "SELECT * FROM users WHERE email = '$email'");
Защищенный код
$stmt = $db->prepare('SELECT * FROM users WHERE email = ?');
$stmt->bind_param('s', $email);
$stmt->execute();
Уязвимый код
email := r.FormValue("email")
// VULN: concatenation
rows, _ := db.Query("SELECT * FROM users WHERE email='" + email + "'")
Защищенный код
email := r.FormValue("email")
rows, _ := db.Query("SELECT * FROM users WHERE email = ?", email)

CSRF (Cross-Site Request Forgery)

Защита от подделки запросов. Основные меры: CSRF-токены, SameSite cookies, проверка Origin/Referer.

Уязвимый код
app.post('/transfer', (req, res) => {
  // VULN: no CSRF check
  transfer(req.body);
  res.send('ok');
});
Защищенный код
const csrf = require('csurf');
app.use(csrf());
app.post('/transfer', (req, res) => {
  transfer(req.body);
  res.send('ok');
});
Уязвимый код
app.post('/transfer', (req, res) => {
  // VULN: no CSRF check
  transfer(req.body);
  res.send('ok');
});
Защищенный код
import csrf from 'csurf';
app.use(csrf());
app.post('/transfer', (req, res) => {
  transfer(req.body);
  res.send('ok');
});
Уязвимый код
@app.post('/transfer')
def transfer_funds():
    # VULN: no CSRF check
    do_transfer(request.form)
    return 'ok'
Защищенный код
from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect(app)

@app.post('/transfer')
def transfer_funds():
    do_transfer(request.form)
    return 'ok'
Уязвимый код
$amount = $_POST['amount'];
// VULN: no CSRF token check
transfer($amount);
Защищенный код
session_start();
if (!hash_equals($_SESSION['csrf'], $_POST['csrf'] ?? '')) {
    http_response_code(403);
    exit('CSRF');
}
transfer($_POST['amount']);
Уязвимый код
func transfer(w http.ResponseWriter, r *http.Request) {
    // VULN: no CSRF check
    doTransfer(r.FormValue("amount"))
}
Защищенный код
csrf := csrf.Protect([]byte(csrfKey))
router.HandleFunc('/transfer', transfer)
// Middleware csrf(router)

CRLF (Header Injection)

Защита от подмены HTTP-заголовков. Основные меры: удалять CR/LF, использовать allowlist.

Уязвимый код
const filename = req.query.name || '';
// VULN: header injection
res.set('X-File', filename);
Защищенный код
const filename = String(req.query.name || '').replace(/[
]/g, '');
res.set('X-File', filename);
Уязвимый код
const filename = String(req.query.name || '');
// VULN: header injection
res.set('X-File', filename);
Защищенный код
const filename = String(req.query.name || '').replace(/[
]/g, '');
res.set('X-File', filename);
Уязвимый код
name = request.args.get('name', '')
# VULN: header injection
resp.headers['X-File'] = name
Защищенный код
name = request.args.get('name', '')
name = name.replace('
', '').replace('
', '')
resp.headers['X-File'] = name
Уязвимый код
$name = $_GET['name'] ?? '';
// VULN: header injection
header("X-File: $name");
Защищенный код
$name = str_replace(["
", "
"], '', $_GET['name'] ?? '');
header("X-File: $name");
Уязвимый код
name := r.URL.Query().Get("name")
// VULN: header injection
w.Header().Set("X-File", name)
Защищенный код
name := strings.NewReplacer("
", "", "
", "").Replace(r.URL.Query().Get("name"))
w.Header().Set("X-File", name)

Path Traversal

Защита от доступа к файлам вне разрешенной директории. Основные меры: нормализация пути, проверка префикса, allowlist.

Уязвимый код
const file = req.query.file;
// VULN: path traversal
res.send(fs.readFileSync('uploads/' + file));
Защищенный код
const base = path.resolve('uploads');
const target = path.resolve(base, req.query.file || '');
if (!target.startsWith(base + path.sep)) return res.sendStatus(403);
res.sendFile(target);
Уязвимый код
const file = String(req.query.file);
// VULN: path traversal
res.send(fs.readFileSync('uploads/' + file));
Защищенный код
const base = path.resolve('uploads');
const target = path.resolve(base, String(req.query.file || ''));
if (!target.startsWith(base + path.sep)) return res.sendStatus(403);
res.sendFile(target);
Уязвимый код
name = request.args.get('file')
# VULN: path traversal
return open('uploads/' + name).read()
Защищенный код
base = os.path.abspath('uploads')
name = request.args.get('file', '')
target = os.path.abspath(os.path.join(base, name))
if not target.startswith(base + os.sep):
    abort(403)
return open(target).read()
Уязвимый код
$file = $_GET['file'] ?? '';
// VULN: path traversal
echo file_get_contents('uploads/' . $file);
Защищенный код
$base = realpath(__DIR__ . '/uploads');
$target = realpath($base . '/' . ($_GET['file'] ?? ''));
if ($target === false || strncmp($target, $base, strlen($base)) !== 0) {
    http_response_code(403);
    exit;
}
echo file_get_contents($target);
Уязвимый код
file := r.URL.Query().Get("file")
// VULN: path traversal
b, _ := os.ReadFile("uploads/" + file)
w.Write(b)
Защищенный код
base, _ := filepath.Abs("uploads")
target, _ := filepath.Abs(filepath.Join(base, r.URL.Query().Get("file")))
if !strings.HasPrefix(target, base+string(os.PathSeparator)) {
    http.Error(w, "forbidden", 403)
    return
}
http.ServeFile(w, r, target)

MITM (Man-in-the-Middle)

Защита от перехвата трафика. Основные меры: только HTTPS, проверка сертификатов, HSTS, TLS-минимум.

Уязвимый код
https.request({
  hostname: 'api.example.com',
  rejectUnauthorized: false // VULN
});
Защищенный код
https.request({
  hostname: 'api.example.com',
  rejectUnauthorized: true,
  minVersion: 'TLSv1.2'
});
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
Уязвимый код
https.request({
  hostname: 'api.example.com',
  rejectUnauthorized: false // VULN
});
Защищенный код
https.request({
  hostname: 'api.example.com',
  rejectUnauthorized: true,
  minVersion: 'TLSv1.2'
});
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
Уязвимый код
requests.get('https://api.example.com', verify=False)  # VULN
Защищенный код
requests.get('https://api.example.com', verify=True)
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
Уязвимый код
$ch = curl_init('https://api.example.com');
// VULN
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
Защищенный код
$ch = curl_init('https://api.example.com');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
Уязвимый код
tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} // VULN
client := &http.Client{Transport: tr}
Защищенный код
tr := &http.Transport{TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS12}}
client := &http.Client{Transport: tr}
w.Header().Set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains')

Brute Force

Защита от перебора паролей. Основные меры: rate limit, задержки, блокировки, 2FA.

Уязвимый код
app.post('/login', (req, res) => {
  // VULN: no rate limit
  authenticate(req.body);
});
Защищенный код
const rateLimit = require('express-rate-limit');
app.use('/login', rateLimit({ windowMs: 60_000, max: 5 }));
app.post('/login', (req, res) => authenticate(req.body));
Уязвимый код
app.post('/login', (req, res) => {
  // VULN: no rate limit
  authenticate(req.body);
});
Защищенный код
import rateLimit from 'express-rate-limit';
app.use('/login', rateLimit({ windowMs: 60_000, max: 5 }));
app.post('/login', (req, res) => authenticate(req.body));
Уязвимый код
@app.post('/login')
def login():
    # VULN: no rate limit
    return auth(request.form)
Защищенный код
from flask_limiter import Limiter
limiter = Limiter(app, key_func=get_remote_address)

@app.post('/login')
@limiter.limit('5/minute')
def login():
    return auth(request.form)
Уязвимый код
// VULN: no rate limit
login($_POST);
Защищенный код
$key = 'login:' . $_SERVER['REMOTE_ADDR'];
$tries = (int)apcu_fetch($key);
if ($tries > 5) { http_response_code(429); exit; }
apcu_store($key, $tries + 1, 60);
login($_POST);
Уязвимый код
func login(w http.ResponseWriter, r *http.Request) {
    // VULN: no rate limit
    auth(r)
}
Защищенный код
limiter := tollbooth.NewLimiter(5, &limiter.ExpirableOptions{DefaultExpirationTTL: time.Minute})
http.Handle('/login', tollbooth.LimitFuncHandler(limiter, login))

Backdoor

Защита от скрытых админ-функций. Основные меры: удалять debug-флаги, отделять админ-панель, строгая авторизация.

Уязвимый код
// VULN: debug backdoor
if (req.query.debug === 'show_users') return res.json(users);
Защищенный код
// Remove debug backdoors
if (!req.user || !req.user.isAdmin) return res.sendStatus(403);
Уязвимый код
// VULN: debug backdoor
if (req.query.debug === 'show_users') return res.json(users);
Защищенный код
if (!req.user?.isAdmin) return res.sendStatus(403);
Уязвимый код
# VULN: debug backdoor
if request.args.get('debug') == 'show_users':
    return jsonify(users)
Защищенный код
if not current_user.is_admin:
    abort(403)
Уязвимый код
// VULN: debug backdoor
if (($_GET['debug'] ?? '') === 'show_users') { echo json_encode($users); }
Защищенный код
if (!is_admin()) { http_response_code(403); exit; }
Уязвимый код
if r.URL.Query().Get("debug") == "show_users" { // VULN
    json.NewEncoder(w).Encode(users)
    return
}
Защищенный код
if !isAdmin(r) { http.Error(w, "forbidden", 403); return }

DDoS (Distributed Denial of Service)

Защита от перегрузки: rate limit, очереди, кэширование, таймауты, CDN/WAF.

Уязвимый код
app.get('/expensive', (req, res) => {
  // VULN: no limits
  res.json(doHeavyWork());
});
Защищенный код
const rateLimit = require('express-rate-limit');
app.use('/expensive', rateLimit({ windowMs: 10_000, max: 20 }));
app.get('/expensive', (req, res) => res.json(doHeavyWork()));
Уязвимый код
app.get('/expensive', (req, res) => {
  // VULN: no limits
  res.json(doHeavyWork());
});
Защищенный код
import rateLimit from 'express-rate-limit';
app.use('/expensive', rateLimit({ windowMs: 10_000, max: 20 }));
app.get('/expensive', (req, res) => res.json(doHeavyWork()));
Уязвимый код
@app.get('/expensive')
def expensive():
    # VULN: no limits
    return jsonify(do_heavy_work())
Защищенный код
@app.get('/expensive')
@limiter.limit('20/10 seconds')
def expensive():
    return jsonify(do_heavy_work())
Уязвимый код
// VULN: no limits
echo json_encode(do_heavy_work());
Защищенный код
$key = 'exp:' . $_SERVER['REMOTE_ADDR'];
$cnt = (int)apcu_fetch($key);
if ($cnt > 20) { http_response_code(429); exit; }
apcu_store($key, $cnt + 1, 10);
echo json_encode(do_heavy_work());
Уязвимый код
func expensive(w http.ResponseWriter, r *http.Request) {
    // VULN: no limits
    json.NewEncoder(w).Encode(doHeavyWork())
}
Защищенный код
limiter := tollbooth.NewLimiter(20, &limiter.ExpirableOptions{DefaultExpirationTTL: 10 * time.Second})
http.Handle('/expensive', tollbooth.LimitFuncHandler(limiter, expensive))

Log4Shell

Защита от уязвимостей логирования. Основные меры: обновление зависимостей, фильтрация опасных шаблонов, запрет lookups.

Уязвимый код
// VULN: logging raw headers
logger.info(`UA: ${req.headers['user-agent']}`);
Защищенный код
const ua = String(req.headers['user-agent'] || '').replace(/\$\{jndi:[^}]+\}/gi, '[redacted]');
logger.info({ ua });
Уязвимый код
// VULN: logging raw headers
logger.info(`UA: ${req.headers['user-agent']}`);
Защищенный код
const ua = String(req.headers['user-agent'] || '').replace(/\$\{jndi:[^}]+\}/gi, '[redacted]');
logger.info({ ua });
Уязвимый код
# VULN: logging raw headers
logger.info(f"UA: {request.headers.get('User-Agent')}")
Защищенный код
ua = (request.headers.get('User-Agent') or '').replace('${jndi:', '[redacted]')
logger.info('UA: %s', ua)
Уязвимый код
// VULN: logging raw headers
error_log('UA: ' . ($_SERVER['HTTP_USER_AGENT'] ?? ''));
Защищенный код
$ua = str_replace('${jndi:', '[redacted]', $_SERVER['HTTP_USER_AGENT'] ?? '');
error_log('UA: ' . $ua);
Уязвимый код
// VULN: logging raw headers
log.Printf("UA: %s", r.Header.Get("User-Agent"))
Защищенный код
ua := strings.ReplaceAll(r.Header.Get("User-Agent"), "${jndi:", "[redacted]")
log.Printf("UA: %s", ua)

Wayback Machine

Защита от доступа к старым версиям страниц. Основные меры: удалять бэкапы из публичной зоны, возвращать 410, ограничивать доступ.

Уязвимый код
// VULN: public backups
app.use('/backup', express.static('backup'));
Защищенный код
app.use('/backup', (req, res) => res.sendStatus(410));
Уязвимый код
// VULN: public backups
app.use('/backup', express.static('backup'));
Защищенный код
app.use('/backup', (req, res) => res.sendStatus(410));
Уязвимый код
# VULN: public backups
app.send_static_file('backup/index.html')
Защищенный код
@app.get('/backup')
def backup():
    abort(410)
Уязвимый код
// VULN: public backups
readfile(__DIR__ . '/backup/index.html');
Защищенный код
http_response_code(410);
exit;
Уязвимый код
// VULN: public backups
http.Handle('/backup/', http.StripPrefix('/backup/', http.FileServer(http.Dir('backup'))))
Защищенный код
http.HandleFunc('/backup/', func(w http.ResponseWriter, r *http.Request){
    w.WriteHeader(410)
})

Terminal Access / Command Injection

Защита от выполнения команд. Основные меры: allowlist, execFile/args, запрет shell.

Уязвимый код
const host = req.query.host;
// VULN: command injection
exec(`ping -c 1 ${host}`);
Защищенный код
const host = String(req.query.host || '');
if (!/^[a-zA-Z0-9.-]+$/.test(host)) return res.sendStatus(400);
execFile('ping', ['-c', '1', host]);
Уязвимый код
const host = String(req.query.host);
// VULN: command injection
exec(`ping -c 1 ${host}`);
Защищенный код
const host = String(req.query.host || '');
if (!/^[a-zA-Z0-9.-]+$/.test(host)) return res.sendStatus(400);
execFile('ping', ['-c', '1', host]);
Уязвимый код
host = request.args.get('host')
# VULN: command injection
os.system(f"ping -c 1 {host}")
Защищенный код
host = request.args.get('host', '')
if not re.match(r'^[a-zA-Z0-9.-]+$', host):
    abort(400)
subprocess.run(['ping', '-c', '1', host], check=True)
Уязвимый код
$host = $_GET['host'] ?? '';
// VULN: command injection
exec('ping -c 1 ' . $host);
Защищенный код
$host = $_GET['host'] ?? '';
if (!preg_match('/^[a-zA-Z0-9.-]+$/', $host)) { http_response_code(400); exit; }
exec('ping -c 1 ' . escapeshellarg($host));
Уязвимый код
host := r.URL.Query().Get("host")
// VULN: command injection
exec.Command("/bin/sh", "-c", "ping -c 1 " + host).Run()
Защищенный код
host := r.URL.Query().Get("host")
if !regexp.MustCompile(`^[a-zA-Z0-9.-]+$`).MatchString(host) {
    http.Error(w, "bad request", 400); return
}
exec.Command("ping", "-c", "1", host).Run()