i want to make a code generator link,like
www.website.com/register?code=29U3HTR3H219URH923UH419H94RH1298491U2HERUH1?plan_bought=LowReseller
in a functions file on php that is redirecting an user on that link.
$planned = htmlspecialchars($_GET["planbought"]);
// connect to database
$db = mysqli_connect('localhost', 'root', 'pass');
mysqli_select_db($db,"ronp");
function generateRandomString($length = 16)
{
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
return substr(str_shuffle(str_repeat($pool, $length)), 0, $length);
}
$code_secure = generateRandomString(17); // OR: generateRandomString(24)
$query = "INSERT INTO codes (code, expired, 'date', plan)
VALUES('$code_secure', '0', date, '$planned')";
mysqli_query($db, $query);
header('Location: register?code=', $code_secure);
?>
Process: After payment,paypal will redirect user on https://website.com/functions_generate-ak9esysgenthos.php?planbought=Low
That link will create a code in database,and will redirect user on https://website.com/register?code_secure=(code)
Now the problem is,i get redirected on "https://website.com/register?code=",not "https://website.com/register?code=(the code created in database,like 'J2498JT9UJ249UTJ293UJ59U123J9RU9U')"
If you look at the documentation for header() you'll see that the second parameter is a boolean value. This parameter specifies if the header should be "forcefully" be replaced. You are incorrectly passing your "secure"* code as that parameter.
What you want to do is concatenate the strings instead of passing your "secure" code as a second parameter. What you want to get is
header('Location: register?code=' . $code_secure);
*The "secure" code you are generating is predictable (as you used this code), if you need a secure code you might want to look into openssl_random_pseudo_bytes before PHP 7.0 and random_bytes() in PHP 7.0 or higher, as demonstrated by this answer.
Furthermore, as mentioned by Raymond Nijland your code is vulnerable to SQL injections. See this excellent answer on how you can prevent SQL injections.
Besides the issues mentioned in Tom Udding's answer, there are at least two other issues:
functions_generate-ak9esysgenthos.php can be accessed without any authentication at all. Moreover, it generates a "secure code" blindly, without determining whether a user is logged in or otherwise authorized to access that page (e.g., without determining whether a payment operation is in progress). This could allow anyone with knowledge of the URL to access functions_generate-ak9esysgenthos.php; depending on how your application is implemented, this could cause orders that weren't paid for or even a denial of service attack (due to the additional order codes clogging your database).
You are generating a random "secure code" without checking whether that code was already used. What if /register?code=... finds two records with the same code? Can your application tolerate the risk of generating the same code for different records? (See also my section on unique random identifiers.)
Related
I apologize if this question has been asked before, if it has just link me to it in a comment.
So I've created a web-service for an android/iOS app with php that works like this.
The app sends a request to http://www.example.com/ws/getCookingData.php with 2 $_POST paramaters userID and foodType
The php file then queries the database using those two variables and returns a json_encode result.
My worry is that if someone were to discover my web-service link they could spam it with post requests resulting in 100's of call to my database that are just un-needed
Below is an example of my current getData.php file
<?php
$userID = mysql_escape_string($_POST['userID']);
$foodType = mysql_escape_string($_POST['foodType']);
$mysqli = getDB();
echo json_encode(getDate($mysqli, $userID, $foodType); //mysql database interaction is here
$mysqli->close();
?>
There is nothing here preventing hackers from attempting to post malicious SQL statements into my database
So what I'm wondering is if I added a 3rd parameter to my post request called appID would this be a good solution?
For example if I were to update my getData.php file to below would this be much more secure or is there a vulnerability I'm missing?
<?php
$appID = $_POST['appID'];
if($appID === "hardCodedEquivalentID"){
$userID = mysql_escape_string($_POST['userID']);
$foodType = mysql_escape_string($_POST['foodType']);
$mysqli = getDB();
echo json_encode(getDate($mysqli, $userID, $foodType); //mysql database interaction is here
$mysqli->close();
}
?>
If this is a good solution or there is already an equivalent practice that would achieve what I'm trying to do please let me know
First of all, use mysqli or PDO instead of mysql function which is deprecated. Secondly create a function which will authenticate the user and see whether the user has the permission to access the data. And thirdly try to LIMIT the data to 100 or so if possible.
Hardcoding the appId is not the solution. Create unique Id's for each specific registered user and then match that appId against that particular user. And when their session is expired clear the access token. And at the start of their session, you can login in them and create new access token and can use the same for their entire session.
To answer your first question
My worry is that if someone were to discover my web-service link they
could spam it with post requests resulting in 100's of call to my
database that are just un-needed
If someone wants to DoS you then you can't do much in your code to prevent it but you may try using service like cloudflare. Not worth worrying about it at the beginning.
About
There is nothing here preventing hackers from attempting to post
malicious SQL statements into my database
then just read documentation on PDO
1- Use mysql_real_escape_string()
2- Use str_replace(" ","",$_POST['userID']) and str_replace("%20","",$_POST['userID'])(Because malicious attacks involves in using %20 and space to inject sql query)
3- Add this line to the top of the page, so the script only takes request if its been from your website (That's what I am using too!)
$referrer = $_SERVER['HTTP_REFERER'];
if (strpos($referrer,"yourwebsite.com")) {
} else {
die();
}
After logging in, I want to redirect users back to the page they were on using PHP. After doing a little searching on the matter and not finding any good solutions, I decided to add a hidden field to the login form which contains the current partial-URL using $_SERVER['REQUEST_URI'].
The server uses this information to redirect the user back to the previous page after logging them in. This has been working correctly, however here is my question.
Overall, what security measures do I need to apply to the url, once it is returned to the server, so that, if tampered with, it will not redirect the user to an external and possibly scrupulous site?
Ex: if I change the value in the form from $_SESSION['REQUEST_URI'] to http://www.google.com, it redirects to Google after login. What is the best way to sanitize this?
*I am currently using mysql_real_escape_string() for SQL injection purposes.
<form action="/signin/" method="post">
<input type="hidden" name="return" value="<?php echo $_SERVER['REQUEST_URI']; ?>" />
</form>
......
$return = mysql_real_escape_string($_POST['return']);
header('Location: '.$return);
As hundreds of other answers/comments have pointed out, everyone should be using mysqli by now. That aside, mysql_real_escape_string() is—as the name implies—for escaping strings that are going to be used in a MySQL query. There is no other use for it. It does not protect you from XSS, as I'll demonstrate...
Setup:
// represent a string without using quotes
function ab($str) {
$out = array();
for ($i = 0; $i < strlen($str); $i++) {
($chr = _ab($str[$i])) || ($chr = 'String.fromCharCode('.ord($str[$i]).')');
}
return implode('+', $out);
}
// represent a character without quotes
function _ab($chr) {
$alpha = array(
'Array', 'Boolean', 'Date', 'Function', 'Iterator', 'Number', 'Object',
'RegExp', 'String', 'ArrayBuffer', 'Float32Array', 'Float64Array', 'JSON',
'Int16Array', 'Int32Array', 'Int8Array', 'Uint16Array', 'Uint32Array',
'Uint8Array', 'Uint8ClampedArray', 'Error', 'EvalError', 'InternalError',
'RangeError', 'ReferenceError', 'StopIteration', 'SyntaxError', 'parseInt',
'TypeError', 'URIError', 'decodeURI', 'decodeURIComponent', 'encodeURI',
'encodeURIComponent', 'eval', 'isFinite', 'isNaN', 'parseFloat', 'uneval'
);
// sort function names by length to minimize output
usort($alpha, function($a, $b){return strlen($a) - strlen($b);});
foreach ($alpha as $fn) {
if (($i = strpos($fn, $chr)) !== false) return "$fn.name[$i]";
}
return false;
}
$mb = chr(0xC2).chr(0x8F); // eats backslashes for breakfast
How it works:
$msg = ab('exploited!');
$payload = "console.log($msg);";
$uri = "$mb\" onmouseover=$payload title=XSS";
// try to sanitize URI
$uri = mysql_real_escape_string($uri);
?>
click me
This generates "URIs" like:
" onmouseover=console.log(Date.name[2]+eval.name[0]+isNaN.name[1]+Date.name[2]); title=hi
which slips right through mysql_real_escape_string() (because the first "character" is actually a partial multibyte character 0xC28F which eats the character immediately after it—the backslash mysql_real_escape_string() inserts to escape the quotation mark), producing:
click me
Which the browser interprets as:
<a title="XSS"" onmouseover="THE_PAYLOAD" href="\">click me</a>
You can test this yourself in Firefox or IE with a console open, and you will see that arbitrary JS code is being executed when you hover over the link, despite the use of mysql_real_escape_string().
mysql_real_escape_string() also doesn't protect you from javascript: links like:
javascript:$.post(%27/account/change-password%27,{newPassword:%27p0wned%27,newPassword2:%27p0wned%27});undefined;
Since percent-encoding isn't used in MySQL, mysql_real_escape_string() doesn't know that %27 is a single-quote or that %22 is a double-quote; so it just ignores them. If the user clicks this link, it'll send a POST request changing their password to one chosen by the attacker. It can be modified to also redirect to the page the user expects to go to, so they'll have no idea of what's just happened.
The only obstacle to this type of attack on your current setup is that URIs are root-relative, so they always start with a "/", but as the first example shows, multibyte strings can be used to trick browsers, and even if the resultant markup is broken, the link will still work due to the leniency of most browsers' HTML parser. This example took me only 15 minutes to come up with after reading your question. A determined attacker could no doubt come up with something that does pose a practical threat.
So to be safe, you have to sanitize your URI using methods specifically designed for HTML and URLs. OWASP's guidelines on URL escaping are a good place to start. Otherwise, the other answers here offer some safer alternatives.
I found this, which looks really good to me: Safely Redirecting with an Open URL Parameter in PHP
He's working with a trusted list of "referers".
Including this, you could work with several ways. Checking the referer, the hidden field in your form and if you're able to, even the server logs.
Except for the server logs, I don't think there's a better way to check where the user came from.
You should store the current url in a session variable like in $_SESSION["last_page_visited"] (except on the login and login submit page). So every time you visit a new page, $_SESSION["last_page_visited"] will be overwritten with the current url. So, no matter which page you visit, the url to last page you visited is saved in the session. And from any page if you click login, after successful login redirect to the url held in that session. And if $_SESSION["last_page_visited"] is empty redirect to index page.
Hope this helps.
If the login page is encrypted with SSL/TLS, and you set the value for the hidden form field from the server (which was verified against your whitelist), then if it gets modified before it's submitted, the user knows about it because they did it.
SSL/TLS is the best practice.
Ok, i'm wondering if this code stored in user_login.php could be improved or if i'm doing it wrong. I'm confused because all my script in the application are long about 30-40 lines only and i was wondering if i am missing something.
This script is called with an ajax call like everyone else in my application except for template files.
<?php
# Ignore
if (!defined('APP_ON')) { die('Silence...'); }
# Gets the variables sent
$user_name = post('user_name');
$user_password = extra_crypt(post('user_password'));
# Check if the user exists
if (!user::check($user_name, $user_password)) { template::bad($lang['incorrect_login']); }
# Logging in
$id = user::get_id($user_name, $user_password);
$u = new user($id);
$u->login();
template::good($lang['correct_login']);
?>
I'm going to explain it:
# Ignore
if (!defined('APP_ON')) { die('Silence...'); }
This basically check that the file is not called directly. Each url is redirected to an index.php file that manage the url (es: www.mysite.com/user/view/id/1) and include the right file. In the first lines of this index.php file there is a define('APP_ON', true);. The index file also initialize the application calling some set of functions and some classes.
# Gets the variables sent
$user_name = post('user_name');
$user_password = extra_crypt(post('user_password'));
The function post() manage the recovering of $_POST['user_name'] making some checks.
The function extra_crypt() just crypt the password using sha1 and a custom alghoritm.
I'm using prepared statement for sql queries so don't worry about escaping post variables.
# Check if the user exists
if (!user::check($user_name, $user_password)) { template::bad($lang['incorrect_login']);
The user class has both static and normal methods. The static ones doesn't require an id to start from, normal methods does. For example user::check(); does check if the username and the password exists in the database.
The template class has just two static methods (template::bad() and template::good()) that manage fast dialog box to send to the user without any header or footer. Instead if you instantiate the class $t = new template('user_view'), the template user_view_body.php is called and you can manage that page with $t->assign() that assign static vars to the template, or with $t->loop() that start a loop etc.
Finally $lang is an array having some common strings in the language set by the user.
# Logging in
$id = user::get_id($user_name, $user_password);
$u = new user($id);
$u->login();
template::good($lang['correct_login']);
At the end we have the actual login. The user class is instantiated and an id is recovered. The user with that id is logged in and we return a template::good() message box to the user.
For any clarification write a comment above.
First of all a message digest function like sha1 is not an encryption function. So please remove crypt from the function name to avoid confusion. Further more, this whole idea of a "custom algorithm" for storing passwords scares the hell out of me. sha256 is a better choice than sha1, but sha1 isn't all that bad after all its still a NIST approved function. Unlike md5 no one has been able to generate a collision for sha1 (although this will happen in the new few years). If you do go with sha1 make sure the salt is a prefix, as to thwart the prefixing attack.
Input validation must always be done at the time of use. the post() function should not be responsible for any input validation or escaping. This is just an incarnation of magic_quotes_gpc which is being removed because its a security train wreak.
Parametrized Query libraries like PDO and ADODB are very good and I recommended using it. This is a great example of sanitizing at the time of use.
Your template assign() method can be used to sanitize for XSS. Smarty comes with a htmlspecialchars output filter module which does this.
With so much functionality behind functions that you haven't provided, it's hard to say whether anything needs to be fixed. Are you making sure the input is clean in post()? Are your database calls secure in user::check()? Are you using session cookies to simplify user prefs/info storage? Is your template class well-written? When you create a new user, are you logging all the necessary information (time of login, IP address if necessary, etc)? Are you ensuring that the user isn't doubly logged in, if that's important here?
The only concrete thing I can tell you about what you wrote is that the sha1 algorithm is completely broken, and you should be using something else instead; see the comments below for suggestions.
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.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
Ok so me and a friend are doing a mini presentation on PHP security (I'm not really into PHP though) and he asked me to find some examples of vulnerable PHP code (one that is prone to SQL injections and all other types of attacks). I was wondering are there any websites with both good and bad pieces of code showing how you should and shouldn't code?
Basically I will put them into our website and he will try to hack it, then we will show the "proper" website and he will try to hack it again.
SQL injection is easy:
$var = $_POST['var'];
mysql_query("SELECT * FROM sometable WHERE id = $var");
This is easily solved by:
$var = mysql_real_escape_string($_POST['var']);
The other common one is XSS (cross site scripting):
$var = $_POST['var'];
echo "<div>$var</div>\n";
allows you to inject Javascript that is run from your site. There are several ways of dealing with this, for example:
$var = strip_tags($_POST['var']);
and
$var = filter_var($_POST['var'], FILTER_SANITIZE_STRING);
A really common beginner's mistake is forget to terminate script execution after a redirect.
<?php
if ($_SESSION['user_logged_in'] !== true) {
header('Location: /login.php');
}
omg_important_private_functionality_here();
The solution:
if ($_SESSION['user_logged_in'] !== true) {
header('Location: /login.php');
exit();
}
This can be missed when testing in a normal browser, because browsers usually follow the Location header without rendering any of the output of the script.
Oh boy, you won't be short of examples. Just Google PHP tutorial and every single one of them has enough holes to fill the Albert Hall.
Result 1, w3schools. What's their first example to include user input?
Welcome <?php echo $_POST["fname"]; ?>!<br />
Bzzt. HTML injection, repeated throughout every piece of example code. What's their first database query?
$sql="INSERT INTO Persons (FirstName, LastName, Age) VALUES ('$_POST[firstname]','$_POST[lastname]','$_POST[age]')";
Bzzt. SQL injection, you lose. Next.
Result 2, official PHP tutorial. What's the first example of outputting a variable?
echo $_SERVER['HTTP_USER_AGENT'];
Bzzt. HTML injection. Not an easily-exploitable one, but still, bad practice of the sort that is repeated throughout php.net's learning materials.
Result 3, tizag.com. What's the first example of echoing user input?
echo "You ordered ". $quantity . " " . $item . ".<br />";
Bzzt.
Result 4, freewebmasterhelp.com. Too basic to include much, but still manages:
print "Hello $name"; // Welcome to the user
Bzzt.
Result 5, learnphp-tutorial.com.
<title><?= $greeting ?> World!</title>
Bz...
I could go on.
Is it any wonder the general quality of PHP code in the wild is so disastrous, when this woeful rubbish is what coders are learning?
Bobby Tables
Bobby Tables is a page devoted to detailing the ways that a script can be vulnerable via SQL injection. This is not unique to PHP, however, SQL injection is the cause of many web page vulnerabilities.
It might be someting you want to include in your presentation.
I've seen code like this written in the past:
foreach ($_REQUEST as $var => $val) {
$$var = $val;
}
It's a way to simulate the maligned register_globals option. It means you can access your variables like this:
$myPostedVar
rather than the terribly more complicated:
$_POST['myPostedVar']
The security risk pops up in situations like this:
$hasAdminAccess = get_user_access();
foreach ($_REQUEST as $var => $val) {
$$var = $val;
}
if ($hasAdminAccess) { ... }
Since all you'd have to do is add ?hasAdminAccess=1 to the url, and you're in.
Another example of a sql-injection-vulnerable login script. This is unfortunately very common among new programmers.
$username = $_POST["username"];
$password = $_POST["password"];
$query = "SELECT username, password
FROM users
WHERE (username = '{$username}')
AND (password = '{$password}')";
Today's DailyWTF:
if(strstr($username, '**')) {
$admin = 1;
$username = str_replace('**', '', $username);
$_SESSION['admin'] = 1;
} else {
$admin = 0;
}
CSRF for the win.
<?php
$newEmail = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
$pdoStatement = $pdoDb->prepare('UPDATE user SET email=:email WHERE ID=:id');
$pdoStatement->execute(array(':email'=>$newEmail, ':id'=>$_SESSION['userId']));
You feel safe with this kind of code. All is good your users can change their emails without injecting SQL because of your code.
But, imagine you have this on your site http://siteA/, one of your users is connected.
With the same browser, he goes on http://siteB/ where some AJAX does the equivalent of this code :
<form method="post" action="http://site/updateMyAccount.php">
<p>
<input name="email" value="badguy#siteB"/>
<input type="submit"/>
</p>
</form>
Your user just got his email changed without him knowing it. If you don't think this kind of attack is dangerous, ask google about it
To help against this kind of attacks, you can either :
Check your user REFERER (far from perfect)
Implement some tokens you had to your forms and check their presence when getting your data back.
Another one is session hijacking. One of the methods to do it is piggybacking.
If your server accepts non cookie sessions, you can have URLs like http://siteA/?PHPSESSID=blabla which means your session ID is blabla.
An attacker can start a session and note his session ID, then give the link http://siteA/?PHPSESSID=attackerSessionId to other users of your website. When these users follow this link, they share the same session as your attacker : a not logged session. So they login.
If the website does not do anything, your attacker and your user are still sharing the same session with the same rights. Bad thing if the user is an admin.
To mitigate this, you have to use session_regenerate_id when your users credentials change (log in and out, goes in administration section etc.).
HTTP Response Splitting attack
If web application is storing the input from an HTTP request in cookie let's say
<?php setcookie("author",$_GET["authorName"]); ?>
It is very prone to HTTP response splitting attack if input is not validated properly for "\r\n" characters.
If an attacker submits a malicious string,such as "AuthorName\r\nHTTP/1.1 200 OK\r\n..",then the HTTP response would be split into two responses of the following form:
HTTP/1.1 200 OK
...
Set-cookie: author=AuthorName
HTTP/1.1 200 OK
...
Clearly,the second response is completely controlled by the attacker and can be constructed with any header and body content instead
Check out the Open Web Application Security Project. They have explanations and examples of lots of different kinds of attacks.
http://www.owasp.org/index.php/Category:Attack
Email header injection attacks are a much bigger pain in the neck then you might suspect (unless you've had to deal with them).
This is very bad:
$to = 'contact#domain.com';
$subject = $_POST["subject"];
$message = $_POST["message"];
$headers = "From: ".$_POST["from"];
mail($to,$subject,$message,$headers);
(code copied from the second reference above.)
The WRONG way to do templates.
<?php
include("header.php");
include($_GET["source"]); //http://www.mysite.com/page.php?source=index.php
include("footer.php");
?>
XSS vulnerabilities are easy to show. Just create a page that puts the value of the GET variable "q" somewhere on the page and then hit the following URL:
http://mysite.com/vulnerable_page.php?q%3D%3Cscript%20type%3D%22javascript%22%3Ealert(document.cookie)%3B%3C%2Fscript%3E
This will cause the user's cookies to be displayed in an alert box.
Allowing upload and not checking extension. Observe:
Site A allows image uploading and displays them.
Cracker guy uploads a file and tricks you to believe its an image file (via HTTP mimetypes). This file has PHP extension and contains malicious code. Then he tries to see his image file and because every PHP extesioned file is executed by PHP, the code is run. He can do anything that apache user can do.
Basic (often security sensitive) operations not working as expected, instead requiring the programmer to use a second "real" version to get non-broken functionality.
The most serious one of these would be where an actual operator is affected: The "==" operator does not work as one would expect, instead the "===" operator is needed to get true equality comparison.
One of the big 3 PHP forum packages was affected by a vulnerability in it's "stay logged in" code. The cookie would contain the user's ID and their password hash. The PHP script would read and cleanse the ID, use it to query the user's correct hash in the database, and then compare it with the one in the cookie to see if they should be automatically logged in.
However the comparison was with ==, so by modifying the cookie, an attacker use a hash "value" of boolean:true, making the hash comparison statement useless. The attacker could thus substitute any user ID to log in without a password.
Allowing people to upload files, whether that API is supposed to be used by users or not. For example, if a program uploads some files to a server, and that program will never upload a bad file, that's fine.
But a hacker could trace what is being sent, and where to. He could find out that it is allowing files to be uploaded.
From there, he could easily upload a php file. Once that's done, it's game over. He now has access to all your data and can destroy or change anything he wants.
Another common mistake is allowing flooding. You should put some sane limits on your data. Don't allow users to input nonsensical data. Why is a user's name 2MB in length? Things like that make it so easy for someone flood your database or filesystem and crash the system due to out of space errors.