Not a true security expert. Just know a thing or two.
I did code audit for many projects...
... and saw the same issues again and again
$_SERVER['HTTP_X_FORWARDED_FOR']
etc.)Filtering is making sure data is valid.
Input is invalid by default unless proven otherwise.
$email = filter_var($email, FILTER_VALIDATE_EMAIL);
if ($email === false) {
// email wasn't valid...
}
// everything's OK
or use libraries and frameworks which are reliable
Making special characters behave like normal characters.
Usually by prefixing with another special character.
Each output has different escaping rules.
A script is injected into the page and is executed in user's browser.
Saw it in most projects I've reviewed.
...
<div>
<?= $_GET['query'] ?>
</div>
...
Escape output.
If need just text.
htmlspecialchars($content, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
If HTML needed.
HTMLPurifier (http://htmlpurifier.org/)
$config = HTMLPurifier_Config::createDefault();
$purifier = new HTMLPurifier($config);
$clean_html = $purifier->purify($dirty_html);
Because you may need to edit it and because data in your own DB could not be trusted.
Third party website can submit forms to your website on behalf of the user.
Introduce CSRF-tokens and use TLS/SSL.
<img src="/logout" />
Using $_REQUEST
is the same.
Executing arbitrary SQL on a project database.
$email = $_POST['email'];
query("SELECT *
FROM user
WHERE email = '$email';");
' OR 1
UNION SELECT 1,'',3,4,5 INTO OUTFILE '1.php' --%20
UNION SELECT 1,LOAD_FILE('config.php'),3,4,5 --%20
Escape queries.
addslashes()
mysql_escape_string()
mysql_real_escape_string()
PDO or driver specific. For PDO:
$stmt = $db->prepare("SELECT * FROM user WHERE email = :email");
$stmt->bindValue(':email', $email);
$user = $stmt->fetch();
Use whitelist.
$allowedTables = ['user_comments', 'post_comments'];
if (!in_array($table), $allowedTables, true) {
return false;
}
// query as usual
Ability to upload and execute code.
Directly execute code.
eval($_GET['query']);
Avoid eval()
or at least use whitelist.
Run PHP with as less permissions possible
require $_GET['type'] . '.php';
Trick user into actually clicking on third party website.
iframe + opactity: 0
Not really PHP related but quite serious.
Disable embedding in a frame via RFC 7034:
header('X-Frame-Options: DENY');
// или
header('X-Frame-Options: SAMEORIGIN');
or via JavaScript:
if (window.top !== window.self) {
document.write = "";
window.top.location = window.self.location;
setTimeout(function () {
document.body.innerHTML = '';
}, 1);
window.self.onload = function (evt) {
document.body.innerHTML = '';
};
}
Save a hash.
Even with salt.
ull brute-forcing of 8-char password hashed as SHA-256 takes...
3.5 days of brute forcing on 2011 single GPU
About 20 hours on 2015 GPU
8x Nvidia GTX 1080 — 2.5 hours
key derivation function iterations = 2^cost
12+ is a safe choice. Yii uses 13.
Cost = 13 → ~28 hashes/sec. on Nvidia GTX Titan X ($700, 2015)
28 * 60 * 60 * 24 = 2419200 hashes a day
6 char lowercase letter-only password = 308915776 combinations
308915776 / 2419200 = 127 days to brute-force a single password
21 days with 6 of such cards. Costs more than ~4200$
Add numbers to 6 char password and to break it in 21 days it would cost more than ~22600$
GPUs are getting better and cheaper. If we don't consider crypto-mining.
AMD R380X (2016) gives you 14 bcrypt hashes with cost=13 per second and costs ~$78.
If you know hashes are leaked:
It would give you more time to react.
Use cookies only. In php.ini:
session.use_cookies = 1
session.use_only_cookies = 1
session_cookie_httponly = 1
session.cookie_secure = 1
Regenerate session ID with session_regenerate_id(true)
after login or permissions change.
Yes, it happens.
Prefer denying everything then allowing what's needed.
HashiCorp Vault
2FA
Limit passwords lifetime (Microsoft does not recommend it)
Used for tokens, reset passcodes, generated passwords, UUID, etc.
Random numbers could be guess-able or could collide if random source isn't good enough.
Tokens, reset passcodes, generated passwords
Use safe random sources
Conduct tests
Have an experienced admin around...
It can't be achieved once and for all.
Have someone familiar with security in the team. Use VCS and do code reviews.
target="_blank"
is unsafeunserialize()
isn't safe!