PHP: Troszkę o Security

About ME

  • devThursday keeper
  • mentor & consultant

 

Contact me:

in/paweł-radzikowski

Paweł Radzikowski

Señor Developer @

Self-proclaimed code magician

PHP: Troszkę o Security

Knowledge Base

  • PHP: The right way
  • Survive The Deep End: PHP Security
  • blog.sucuri.net
  • acunetix.com
  • owasp.org

User Authentication 

<?php

$username = $_POST['username'];
$password = $_POST['password'];

$userPassword = getUserPassord($username);
$hashPassword = sha1($password);

if($userPassword === $hashPassword) {
	exit('Success');
}

echo 'Fail';
Why are common hashing functions such as md5() and sha1() unsuitable for passwords?Hashing algorithms such as MD5, SHA1 and SHA256 are designed to be very fast and efficient. With modern techniques and computer equipment, it has become trivial to "brute force" the output of these algorithms, in order to determine the original input.

Because of how quickly a modern computer can "reverse" these hashing algorithms, many security professionals strongly suggest against their use for password hashing.

password_hash()

  • bcrypt - PHP5.5
  • Argon2i - PHP7.2
  • Argon2id - PHP7.3

crypt()

  • MD5 crypt
  • Standard DES
  • Extended DES
  • Blowfish

Rainbow Table

<?php

$username = $_POST['username'];
$password = $_POST['password'];

$userPassword = getUserPassord($username);
$hashPassword = password_hash($password, PASSWORD_ARGON2ID);

if($userPassword === $hashPassword) {
	exit('Success');
}

echo 'Fail';

===

password_verify

hash_equals

<?php

$username = $_POST['username'];
$password = $_POST['password'];

$userPassword = getUserPassord($username);

if(password_verify($password, $userPassword)) {
	exit('Success');
}

echo 'Fail';

Cross Site Scripting (XSS)

<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<script>...NEVER PUT UNTRUSTED DATA HERE...</script>
<style>...NEVER PUT UNTRUSTED DATA HERE...</style>
</head>
<body>
  <!--...NEVER PUT UNTRUSTED DATA HERE...-->
  <div ...NEVER PUT UNTRUSTED DATA HERE...=test />
  <a href="EVER PUT UNTRUSTED DATA HERE..." />
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<script>alert('...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...')</script>
  <style>background-url : "javascript:alert('...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...')";</style>
</head>
<body>
  <!----><script>alert('...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...')</script>-->
  <div onload=alert('...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...') />
  <a href="javascript:alert('...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...')" />
</body>
</html>

HTML entities

 & --> &amp;
 < --> &lt;
 > --> &gt;
 " --> &quot;
 ' --> &#x27;     
 / --> &#x2F;

PHP func

htmlspecialchars();


htmlspecialchars_decode();

{ background-url : "javascript:alert('HEHE')"; }  // and all other URLs
{ text-size: "expression(alert('HEHE'))"; }   // only in IE
<a href="javascript:alert('HEHE')">Click me</a>

Zawsze waliduj dane !

Welcome to XXI

Content-Security-Policy.com

Dyrektywy

  • default-src
  • script-src
  • style-src
  • img-src
  • media-src
  • connect-src
  • report-uri
  • form-action
  • frame-ancestors

Źródła

  • *
  • 'none'
  • 'self'
  • domain.example.com
  • *.example.com
  • https://cdn.com
  • https:
  • 'unsafe-eval'
  • 'unsafe-inline'
  • 'nonce-XXXX'
Content-Security-Policy: default-src 'none'; script-src 'self'; connect-src 'self';	img-src 'self'; style-src 'self';

Injections

  • SQL
  • OS command
 

SQL Injection

$dbh->query("SELECT * from admin WHERE login='".$login."' AND password='".$password."';");
SELECT * from admin WHERE login='login' AND password='password' OR login='login'; -- ';

Jak się obronić?

  • Walidacja
  • Escape Input
  • Parametryzacja zapytań
<?php

if(isset($_POST)) {
    switch($_POST['type']) {
    	case 1: 
            $type = 1;
        	break;
        case 2: 
            $type = 2;
            break;
        default: $type = 0;
    }
    $users=$dbh->query("SELECT * from admin WHERE type=".$type);
}
<?php
$mysqli = new mysqli("localhost","***", "***", "***");
if(isset($_POST)) {
    $login=$mysqli->real_escape_string($_POST['login']);
    $password=$mysqli->real_escape_string($_POST['password']);
    $result=$mysqli->query("SELECT * from admin WHERE login={$login} AND password={$password}");
if($result && $result->fetch_array()) {
    echo 'Zalogowany';
} else {
    echo 'Niezalogowany';
}
<?php
$dbh = new PDO('mysql:host=localhost;dbname=***', '***', '***');
if(isset($_POST)) {
    $login=$_POST['login'];
    $password=$_POST['password'];
    $user=$dbh->prepare("SELECT * from admin WHERE login=:login AND password=:password");
    $user->bindValue('login', $login, PDO::PARAM_STR);
    $user->bindValue('password', $password, PDO::PARAM_STR);
    $user->execute();
if($user->fetchColumn()) {
    echo 'Zalogowany';
} else {
    echo 'Niezalogowany';
}
PREPARE stmt1 FROM 'SELECT * from admin where login=? AND password=?';
SET @login = "admin";
SET @password = "wrongpassword' OR login='admin";
EXECUTE stmt1 USING @login, @password;
DEALLOCATE PREPARE stmt1;

OS Command Injection

<?php
exec("ping -c 4 " . $_GET['host'], $output);
echo "<pre>";
print_r($output);
echo "</pre>";
<?php
exec(escapeshellcmd("ping -c 4 " . escapeshellarg($_GET['host'])), $output);
echo "<pre>";
print_r($output);
echo "</pre>";

Path Traversal

<?php
//http://example.com/images/XXX.jpg
readfile("images/" . $_GET["file"]);
<?php
//http://example.com/images/../../etc/passwd
readfile("images/" . $_GET["file"]);

root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/usr/bin/nologin
daemon:x:2:2:daemon:/:/usr/bin/nologin
mail:x:8:12:mail:/var/spool/mail:/usr/bin/nologin
ftp:x:14:11:ftp:/srv/ftp:/usr/bin/nologin
http:x:33:33:http:/srv/http:/usr/bin/nologin
nobody:x:99:99:nobody:/:/usr/bin/nologin
systemd-journal-gateway:x:191:191:systemd-journal-gateway:/:/usr/bin/nologin
systemd-timesync:x:192:192:systemd-timesync:/:/usr/bin/nologin
systemd-network:x:193:193:systemd-network:/:/usr/bin/nologin
systemd-bus-proxy:x:194:194:systemd-bus-proxy:/:/usr/bin/nologin
systemd-resolve:x:195:195:systemd-resolve:/:/usr/bin/nologin
<?php
$_GET['file'] = '../../etc/passwd';
readfile("images/" . $_GET["file"]);
  • Walidacja
  • Kontrola dostępu do zasobów dysku
  • Stosowanie identyfikatorów zamiast nazw
Payload Reprezentacja
%2e%2e%2f ../
%2e%2e/ ../
..%2f ../
%2e%2e%5c ..\
%252e%252e%255c ..\
..%255c ..\
..%c0%af ..\
..%c1%9c ..\

Q/A

PHP: Troszkę o Security

By Paweł Radzikowski

PHP: Troszkę o Security

  • 229