SQL injection vulnerabilities arise when you make unsafe database queries. Simply put, users can view your website’s database by entering a query into the URL or filling in the form. Don’t take SQL injection lightly because it’s in Top 10 web security vulnerabilities according to OWASP 2020 announcement there.
Join the channel Telegram of the AnonyViet 👉 Link 👈 |
How to exploit SQL Injection vulnerability
The simplest is to use the SQLi auto exploit tool. I usually use SQLmap, because according to experience this is the tool with the best possible exploitation. But the downside is that you have to use commands instead of interfaces.
Or you can use the tool SQL Dumper has an interface with more features that are easier to set up. If you are new to learning, you can try this tool to exploit the Website’s database.
How to prevent SQL injection vulnerabilities?
The best way to prevent SQL injection vulnerabilities is to use a framework that allows you to securely filter input data before it enters the database. ORM (Object Relational Mapper) is a good option that you should try. For additional layers of security, validate all input and use WAF (Web Application Firewall).
Simple example
Let’s say I have a Java application that allows users to retrieve their documents by ID. I can do it like this:
String query = "SELECT * FROM documents WHERE ownerId=" + authContext.getUserId() + " AND documentName="" + request.getParameter("docName") + """;
executeQuery(query);
If the user ID is 25 and the URL is https://www.example.com/documents/?docName=ABC123, the query would be:
SELECT * FROM documents WHERE ownerId=25 AND documentName="ABC123";
Still fine, right? But what if the URL is https://www.example.com/documents/?docName=ABC123’OR’1’=’1?
Now I will get the following query that returns all documents of all users (because 1 = 1 is always true):
SELECT * FROM documents WHERE ownerId=25 AND documentName="ABC123" OR '1'='1';
So how to avoid this error?
Using Object Relational Mapping
Taking Java as an example, using an ORM such as hibernate to implement JPA (Java Persistence API) might look like this.
First, define the model.
@Entity
public class Document {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String documentName;
private Integer ownerId;
}
Then define the class repository.
@Repository
public interface DocumentRepository extends JpaRepository<Document, Long> {
List<Document> findByDocumentNameAndOwnerId(String documentName, Integer ownerId);
}
Finally, you can use the repository and fetch the documents as follows:
List<Document> docs = documentRepository.findByDocumentNameAndOwnerId(request.getParameter("docName"), authContext.getUserId());
The ORM will handle all parameters safely. Now suppose you want more control over the queries. In that case, many ORMs provide query builder you can use, such as the Hibernate Criteria API.
If you use Python, Django has an equally great ORM; If you don’t use Django, sqlalchemy is a great option.
PHP has Doctrine. You just need to google to search for ORMs that match the technology of your choice.
Warning
ORM frameworks are not 100% perfect.
The first is that they still have the functionality to support raw SQL queries/query parts. You just need to avoid using those features.
The second is that ORM frameworks often have security holes, just like any other software package. So learn other good practices: validate all input data, use WAF, update packages…
Prepared statements
Prepared statements are a more manual choice and should be avoided because compared to ORMs, it has a significantly higher risk of human error. However, this still beats the simple string concatenation method (like the example above). This approach looks like this:
String query = "SELECT * FROM documents WHERE ownerId=? AND documentName = ?";
PreparedStatement ps = conn.prepareStatement(query);
ps.setString(1, authContext.getUserId());
ps.setString(2, request.getParameter("docName"));
ResultSet rs = ps.executeQuery();
In theory, this is pretty safe. However, in my experience, as the codebase grows larger, mistakes will start to appear. You only need one mistake to be completely attacked. Cases like arrays (documentId IN (“foo”, “bar”)) are where devs often make mistakes.
So if you decide to use this approach, be careful with it when you extend the codebase.
Web Application Firewall
WAF products should not be considered a good SQL injection control. But they are a great extra layer of security and are often quite effective against SQL injection attacks.
A great open source solution is to deploy Apache with ModSecurity CRS in front of your webapp.
Database Firewall
Depending on your database and budget, you might consider trying out database firewalls. I have never tried this, but you can also find out the link below, maybe it will help you.
Conclusion
SQL injection is a simple injection vulnerability. And like all security holes, you can prevent it by using an appropriate library or framework for building the protocol, in this case SQL.
ORM is safer than prepared statements. And if you don’t need too much control over the queries, use a lower level ORM commonly known as a query builder. WAF can add an extra layer of security, but you should never rely on it for security.