I have to work on an old site with major security issues : SQL Injections are really easy to perform.
Obviously, the best way to prevent this kind of attacks is to escape what is used in query (prepare statements with PDO, mysql_real_escape_string with MySql, etc.) but we can't do that quickly : the whole site is procedural PHP (no class), the queries are "prepared" everywhere, there are hundreds of pages and thousands of users everyday, and a new version wil come as soon as possible.
So since this morning the following function is called on each request to detect suspicious POST or GET parameters based on keywords.
const SQLI_UNSAFE = 3;
const SQLI_WARNING = 2;
const SQLI_SAFE = 1;
const SQLI_MAIL_DEST = 'monmail#mondest.com';
function sqlicheck() {
$params = array_merge($_GET, $_POST);
$is_warning = false;
foreach($params as $key=>$param) {
switch(getSafeLevel($param)) {
case SQLI_SAFE:
break;
case SQLI_WARNING:
$is_warning = true;
break;
case SQLI_UNSAFE:
mail(SQLI_MAIL_DEST, 'SQL INJECTION ATTACK', print_r($_REQUEST, true).' '.print_r($_SERVER, true));
header('Location: http://monsite/404.php');
exit();
}
}
if($is_warning === true) {
mail(SQLI_MAIL_DEST, 'SQL INJECTION WARNING', print_r($_REQUEST, true).print_r($_SERVER, true));
}
}
function getSafeLevel($param) {
$error_words = array('select%20','drop%20','delete%20','truncate%20','insert%20','%20tbclient','select ','drop ','delete ','truncate ','insert ',);
$warning_words = array('%20','select','drop','delete','truncate', ';','union');
foreach($error_words as $error_word) {
if(stripos($param, $error_word) !== false) return SQLI_UNSAFE;
}
foreach($warning_words as $warning_word) {
if(stripos($param, $warning_word) !== false) return SQLI_WARNING;
}
return SQLI_SAFE;
}
This seems to detect some kinds of attacks but it's clearly very basic. Any ideas to improve it? Any major issue ?
First, make sure that the database user executing the queries only has select, update, delete permissions. If the user can't execute drop, there's no way it'll happen (this assumes that your users will never need to create or drop tables, but if they do, you can create table level permissions to protect the big tables).
Second, your script will only tell you what people are using; it won't do a site wide check of what queries are possible; if there's a section of your site that isn't used much, you won't get any mail telling you. Better to just comb through the code with a search tool.
After that, you have to start modifying the code and doing escaping and validation, and that's just going to take a while.
Why not just use real_escape_string() htmlentities() stripslashes and filter classes of php and log the SQL queries so that you can see what people are sending you, use sha256 with some md5 you will be ok other thing is for quick action just send login data in binary format
Related
I need to protect my web application against xss attacks via URL.
what is the best way to do this?
the application is huge, so I can not edit each of the actions, need something general.
Examples:
http://example.com/[anyproductpage.html]?source=alert('Xss')
http://example.com/catalog/?baseUrl=alert('Xss')&q=1
http://example.com/catalog/productimagezoom?index=alert('Xss')
If you aim to manipulate your actions before handle them you can use beforeAction in your controller/component, with something like this:
protected function beforeAction($action) {
#check a preg_match on a url sanitization pattern, like "[^-A-Za-z0-9+&##/%?=~_|!:,.;\(\)]", for instance
return parent::beforeAction($action);
}
This articles shows how you can make your application secure with SQL Injections, XSS Attacks and CSRF.
Hope it helps you.
Firstly, you can use regular expressions to validate your inputs, you can generalize your inputs in some regular expresions, something like this:
$num = $_GET["index"];
if (preg_match("^\d{2}$", $num)) {
//allowed
} else {
//not allowed
}
Also you can create a white list or black list, if your inputs can be grouped into what is allowed in your application, use a white list, otherwise use a black list. This lists can be sotored in your database or files, something you can easily upgrade without affecting your application. You just have to compare your inputs with that list before proccesing your inputs.
My last recommendation is encoding, you should encode your inputs and outputs, because your data from database can contain malicious code, maybe someone put it by mistake or mischief, always think in all possibilities. For this case, I remember the functions htmlspecialchars and utf8_encode, I think you should the first function, also you can analyze your inputs and build your own encoding function.
I share the following links:
http://php.net/manual/es/function.preg-match.php
http://php.net/manual/es/function.utf8-encode.php
http://php.net/manual/es/function.htmlspecialchars.php
https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet
I hope this information helps you.
Good Luck.
for all actions in same just add a event handle to onBeginRequest:
$this->attachEventHandler('onBeginRequest', [$this, 'purifyParams']);
public function purifyParams($event)
{
$userAgent = $this->getRequest()->getUserAgent();
$isIE = preg_match('/(msie)[ \/]([\w.]+)/i', $userAgent, $version);
if (!empty($isIE) && (int) $version[2] < 11) {
$purifier = new CHtmlPurifier();
$_GET = array_map(function ($param) use ($purifier) {
return $purifier->purify($param);
}, $_GET);
}
}
I am relatively new to web development. And just today my teacher told me to implement a method to avoid cross server scripting in my project. I have searched a lot and found about xss_clean but i am not sure how to use and implement it. Any guide or article related to that would be welcome. Or if you want to explain here that would save me a lot of extra hard work i will be very thankful to you. I am using CodeIgniter framework so please any article related to that would be good.
The problem with XSS vulnerabilities is that you can't just apply one fix. These vulnerabilities can occur in every little thing you write.
XSS happens when an attacker is able to inject HTML into the page of a visitor of your website. Depending on what that code is he could send the visitor to his phishing website or steal his session.
It's not hard to imagine how you can prevent HTML injection: you just HTML-escape all user input. But in practice there can be quite a bit of this and your system is only as secure as the weakest link.
From my experience, CI's XSS is pretty good -- I have run into situations where it does remove something which I have wanted which can be a pain to debug if you're not expecting it. I've never been able to "easily" circumvent it nor have I read about any exploits (and the CI community is fairly large).
For details take a look below link which may help you
http://blog.astrumfutura.com/2011/05/codeigniter-2-0-2-cross-site-scripting-xss-fixes-and-recommendations/
Read this also https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet
http://htmlpurifier.org/
for removing xss attack you can do following things:
you need to remove unwanted tag like "script" from post and get value.
remove meta-characters from post and get value.
a very short and simple answer use following line of code where you are submitting your details that mince in your controller file.
$data = $this->security->xss_clean($data);
here data may your single value or array in case you have multiple records.
one more addon to your query.use following to protect from sql injection
$username = $this->input->post('username');
$query = 'SELECT * FROM subscribers_tbl WHERE user_name = '.
$this->db->escape($email);
$this->db->query($query);
I have implemented this functionality for protection against XSS attacks:
public static function protectArrayAgainstXSS(&$arr) {
$changed = false;
foreach ($arr as $index => $a) {
if (is_array($a)) {
$changed = $changed || Common::protectArrayAgainstXSS($arr[$index]);
} else if ($a !== null) {
$changed = $changed || ($arr[$index] === strip_tags($a));
$arr[$index] = strip_tags($a);
}
}
return !$changed;
}
I am adding some server-side form validations (using php) in case one of the users of my site has javascript turned off. On one form, there are 10 separate input fields that can be changed. Could someone please tell me which protocol will use less system resources? In the first, I write some mySQL variables to check the user's current settings, and compare these with the posted settings. If all 10 posted values are identical to the current values, don't UPDATE database, else UPDATE the database:
$login_id = $_SESSION['login_id'];
$sql1 = mysql_fetch_assoc(mysql_query("SELECT value1 FROM login WHERE login_id =
'$login_id'"));
$sql1a = $sql1['value1'];
// Eight More, then
$sql10 = mysql_fetch_assoc(mysql_query("SELECT value10 FROM login WHERE login_id =
'$login_id'"));
$sql10a = $sql10['value10'];
$Value1 = $_POST['Value1'];
// Eight More, then
$Value10 = $_POST['Value10'];
//Other validations then the following
if (($sql1a == $Value1)&&($sql2a == $Value2)&&.......($sql10a == $Value10)) {
echo "<script>
alert ('You haven't made any changes to your profile');
location = 'currentpage.php';
</script>";
}
else {
$sqlUpdate = mysql_query("UPDATE login SET value1 = '$Value1',....value10 = '$Value10'
WHERE login_id = '$login_id'");
echo "<script>
alert ('Your profile has been updated!');
location = 'currentpage.php';
</script>";
}//End php
OR is it less expensive to just use the user-posted values (keep the $_POST variables) and avoid checking with the comparison line: (($sql1a == $Value1)&&($sql2a == $Value2)&&.......($sql10a == $Value10)) and just go right to
//Other validations then the following
$sqlUpdate = mysql_query("UPDATE login SET value1 = '$Value1',....value10 = '$Value10'
WHERE login_id = '$login_id'");
echo "<script>
alert ('Your profile has been updated!');
location = 'currentpage.php';
</script>";
Thanks for any input on this!
If I understand correctly, your question is whether it's OK for performance to check the profile for modifications. For me, after I've checked your code, this is about much more than just performance...
Let's start with the performance: AFAIK MySQL queries are slower than basic PHP comparisions, that's true - but on this scale, I really don't think it matters much. We're talking about two very basic queries which won't handle a lot of data.
Let's think about what the end user will see (UX): in the second scenario, the user will not have the most exact feedback telling him/her that no modification has been done. On a profile modification screen, I suppose that might not be intentional, so I would tell that we haven't modified anything. (Also, performing an unnecessary UPDATE query is not the most elegant.)
#aehiilrs is right, please pay attention to that comment. This style of MySQL usage is particularly bad for security - if you keep going with this, you will create a lot of security holes in your PHP code. And those are really easy to discover and exploit, so please, have a good look on the alternatives, starting with PDO as mentioned. Any good PHP book out there will show you the way. You can also have a look at a great Q/A here on StackOverflow: How can I prevent SQL injection in PHP?
I wonder whether it's a good idea to try to update the user interface like you did - I would strongly prefer loading another PHP without any <script> magic in the output. In the result PHP, you can always display something like a CSS-styled statusbar for displaying info like that.
We have some problems with users performing a specific action twice, we have a mechanism to ensure that users can't do it but somehow it still happens. Here is how our current mechanism works:
Client side: The button will be disabled after 1 click.
Server side: We have a key hash in the URL which will be checked against the key stored in SESSIONS, once it matches, the key is deleted.
Database side: Once the action is performed, there is a field to be flagged indicating the user has completed the action.
However, with all these measures, still there are users able to perform the action twice, are there any more safer methods?
Here is the partial code for the database side:
$db->beginTransaction();
// Get the user's datas
$user = $db->queryRow("SELECT flag FROM users WHERE userid = {$auth->getProperty('auth_user_id)}");
if ($user['flag'] != 0) {
$db->rollback();
// Return with error
return false;
}
// Proceed with performing the action
// --- Action Here ---
// Double checking process, the user data is retrieved again
$user = $db->queryRow("SELECT flag FROM users WHERE userid = {$auth->getProperty('auth_user_id)}");
if ($user['flag'] != 0) {
$db->rollback();
// Return with error
return false;
}
// --- The final inserting query ---
// Update the flag
$db->query("UPDATE users SET flag = 1 WHERE userid = {$auth->getProperty('auth_user_id)}");
$db->commit();
return true;
It is good to see that you have taken all measures to defeat the bad guys. Speaking in terms of bad guys:
Client side: This can easily be bypassed by simply disabling javascript. Good to have anyways but again not against bad guys.
Server side: This is important, however make sure that you generate a different hash/key with each submission. Here is a good tutorial at nettutes on how to submit forms in a secure fashion.
Database side: Not sure but I suspect, there might be SQL injection problem. See more info about the SQL Injection and how to possibly fix that.
Finally:
I would recommend to you to check out the:
OWASP PHP Project
The OWASP PHP Project's goal (OWASP PHP Project Roadmap) is to enable developers, systems administrators and application architects to build and deploy secure applications built using the PHP programming language.
Well the JS method and Hash method may be cheated by some notorious guy, but 3rd method seems to be very good in order to protect the redundancy. There must be some programming flaw to get passed this.
Why don't u just check the flag field on the page where you are inserting the values rather than where user performing the action (if you are doing it now)
Pseudocode follows:
<?
$act_id; // contains id of action to be executed
$h = uniqid('');
// this locks action (if it is unlocked) and marks it as being performed by me.
UPDATE actions SET executor = $h WHERE act_id = $act_id AND executor = '';
SELECT * FROM actions WHERE executor = $h;
//
// If above query resulted in some action execute it here
//
// if you want to allow for executing this exact action in the future mark it as not executed
UPDATE actions SET executor = '' WHERE act_id = $act_id;
Important things:
First query should be update claiming
the action for me if it is yet
unclaimed.
Second should be query
grabbing action to execute but only
if it was claimed by me.
I thought that I should use JSON for ID/pass storing format once, but I reserched about it, then I finally found that JSON is too difiicult to me, so now I am considering to use CSV.
The CSV file would be like this. File name is id.csv.
aaa_id,aaa_pass
bbb_id,bbb_pass
ccc_id,ccc_pass
Left colum is id, and right colum is password and each infos are separated by commas. Login form is login.php. This is my sample for login.php.
<form method="post" action="login-process.php">
id <input type="text" name="id" size="20"><br>
pass <input type="text" name="pass" size="20"><br>
<input type="submit" value="login">
</form>
And now I need to write login-process.php
<?php
error_reporting(0);
$handle = fopen("id.csv", "r");
while (($data = fgetcsv($handle)) !== FALSE) {
print $data['0'];
print "=>";
print $data['1'];
print "<br>";
}
?>
When I excute this script, it shows as:
aaa_id=>aaa_pass
bbb_id=>bbb_pass
ccc_id=>ccc_pass
I feel I am closing to final goal, but I need to write authenticate code.
How do I improvement of this code?
Firstly, I agree with what Paolo says, this is definately the wrong approach. In addition to what he said:
The login process will get slower and
slower as you have more users in the
file (this will probably be true to an extent for any system, but I think it will be significant here)
Making changes in the middle of the
file will be a complete pain
Having
multiple script instances trying
write to the file simultaneously will
probably result in corruption/missing
data
However, if you were to have a form with fields called 'username' and 'password', you could process it like below. I am debating whether I should remove the code in order to avoid the risk of people actually writing stuff like this.
<?php
$username = trim($_POST['username']);
$password = trim($_POST['password']);
if (!strlen($username) || !strlen($password)) {
die('Please enter a username and password');
}
$success = false;
$handle = fopen("test.csv", "r");
while (($data = fgetcsv($handle)) !== FALSE) {
if ($data[0] == $username && $data[1] == $password) {
$success = true;
break;
}
}
fclose($handle);
if ($success) {
// they logged in ok
} else {
// login failed
}
You probably will want to read up on using Sessions to remember that the user is logged in across multiple page requests.
Since you didn't cover what I consider to be an astoundingly obvious question to ask, I must ask it:
Why aren't you using a database for this? PHP and MySQL have a beautiful, beautiful relationship that is borderline insulting to ignore for a situation like this. Unless you have a particularly especial circumstance that makes databases unavailable to you - which I honestly can't think of off the top of my head - you should go ahead and switch your code to use databases. They are much faster, much easier, and much better. For the purposes of this site, though, a tutorial on how to do it would be rather lengthy. You should consider reading up on the many online tutorials on this subject or perhaps buy a book.
EDIT:
Avoiding databases because you have to worry about SQL injections is like avoiding making a website at all because you have to worry about XSS attacks, or avoiding writing code at all because you could write a bug. Although SQL injections are popular security holes due to the many naive programmers out there, it is trivial to protect yourself against them by using prepared statements in MySQLi or PDO.
As a side note, storing passwords as plain text is a horrible idea. The proper way to do it is to do a one-way encryption of the password upon insertion into the database, and whenever you want to check if someone provided the correct password you encrypt that password and compare it to the one in the database. If you don't do that, you're just asking for trouble.