<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Siber Güvenlik arşivleri - Ali Karahisar</title>
	<atom:link href="https://alikarahisar.com/blog/category/siber-guvenlik/feed/" rel="self" type="application/rss+xml" />
	<link>https://alikarahisar.com/blog/category/siber-guvenlik/</link>
	<description>Yazılım ve AI Hakkında Karalamalar</description>
	<lastBuildDate>Thu, 29 Oct 2026 07:37:00 +0000</lastBuildDate>
	<language>tr</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.4</generator>

<image>
	<url>https://alikarahisar.com/blog/wp-content/uploads/2026/08/cropped-Code_Generated_Image-32x32.png</url>
	<title>Siber Güvenlik arşivleri - Ali Karahisar</title>
	<link>https://alikarahisar.com/blog/category/siber-guvenlik/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>SQL Injection: A Friendly Guide to Understanding the Threat</title>
		<link>https://alikarahisar.com/blog/sql-injection-a-friendly-guide-to-understanding-the-threat/</link>
					<comments>https://alikarahisar.com/blog/sql-injection-a-friendly-guide-to-understanding-the-threat/#respond</comments>
		
		<dc:creator><![CDATA[Ali Karahisar]]></dc:creator>
		<pubDate>Thu, 29 Oct 2026 07:37:00 +0000</pubDate>
				<category><![CDATA[Siber Güvenlik]]></category>
		<category><![CDATA[Yazılım]]></category>
		<category><![CDATA[Blind SQL Injection]]></category>
		<category><![CDATA[How SQL Injection works]]></category>
		<category><![CDATA[JDBC SQL Injection prevention]]></category>
		<category><![CDATA[OWASP SQL Injection]]></category>
		<category><![CDATA[Parameterized queries]]></category>
		<category><![CDATA[Prepared statements]]></category>
		<category><![CDATA[Secure database queries]]></category>
		<category><![CDATA[SQL Injection attack]]></category>
		<category><![CDATA[SQL Injection examples]]></category>
		<category><![CDATA[SQL Injection in Java]]></category>
		<category><![CDATA[SQL Injection prevention]]></category>
		<category><![CDATA[SQL Injection vulnerability]]></category>
		<category><![CDATA[What is SQL Injection]]></category>
		<guid isPermaLink="false">https://alikarahisar.com/blog/?p=130</guid>

					<description><![CDATA[<p>Imagine typing your email address into a website&#8217;s login form. The application takes that value, searches its database, and finds your account. Nothing unusual so far. Now imagine entering a piece of SQL instead of an email address. What happens next depends on how the developer built the database query. If the application inserts your [&#8230;]</p>
<p><a href="https://alikarahisar.com/blog/sql-injection-a-friendly-guide-to-understanding-the-threat/">SQL Injection: A Friendly Guide to Understanding the Threat</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Imagine typing your email address into a website&#8217;s login form. The application takes that value, searches its database, and finds your account.</p>



<p class="wp-block-paragraph">Nothing unusual so far.</p>



<p class="wp-block-paragraph">Now imagine entering a piece of SQL instead of an email address. What happens next depends on how the developer built the database query.</p>



<span id="more-130"></span>



<p class="wp-block-paragraph">If the application inserts your input directly into its SQL statement, you might change what the database executes.</p>



<p class="wp-block-paragraph">That&#8217;s the problem behind <strong>SQL Injection</strong>, often shortened to SQLi.</p>



<p class="wp-block-paragraph">Although SQL Injection has been around for decades, developers still encounter it in modern web applications, REST APIs, backend services, and systems that use object-relational mapping frameworks.</p>



<p class="wp-block-paragraph">The good news? Developers can prevent most SQL Injection vulnerabilities through a few reliable coding practices.</p>



<p class="wp-block-paragraph">This guide explains how SQL Injection works, why it matters, and how to write safer Java database code.</p>



<h2 class="wp-block-heading">What Is SQL Injection?</h2>



<p class="wp-block-paragraph">SQL Injection is a security vulnerability that allows an attacker to interfere with a database query by supplying specially crafted input.</p>



<p class="wp-block-paragraph">The vulnerability appears when an application mixes untrusted data with SQL commands.</p>



<p class="wp-block-paragraph">Consider a database query that retrieves a customer by email address:</p>



<pre class="wp-block-code language-sql"><code>SELECT id, email, full_name
FROM customers
WHERE email = 'alice@example.com';</code></pre>



<p class="wp-block-paragraph">This query looks for a specific customer.</p>



<p class="wp-block-paragraph">But what if the application builds the query by joining SQL text with whatever the user enters?</p>



<p class="wp-block-paragraph">An attacker may then influence the query&#8217;s logic instead of simply providing a value.</p>



<p class="wp-block-paragraph">Depending on the application, database permissions, and query structure, SQL Injection can enable attackers to:</p>



<ul class="wp-block-list">
<li>Access sensitive customer information</li>



<li>Read data that should remain private</li>



<li>Modify or delete database records</li>



<li>Bypass certain authentication checks</li>



<li>Interfere with application business logic</li>



<li>Disrupt database availability</li>
</ul>



<p class="wp-block-paragraph">Some database configurations can expose additional capabilities when the application connects with excessive privileges.</p>



<p class="wp-block-paragraph">The OWASP Top 10:2025 includes SQL Injection within <strong>A05:2025 – Injection</strong>, a broader category covering injection vulnerabilities.</p>



<p class="wp-block-paragraph">The important distinction is that SQL itself isn&#8217;t the problem.</p>



<p class="wp-block-paragraph">The vulnerability comes from how an application handles SQL commands and external data.</p>



<h2 class="wp-block-heading">How Does SQL Injection Work?</h2>



<p class="wp-block-paragraph">Let&#8217;s examine a small Java application.</p>



<p class="wp-block-paragraph">Suppose an online store allows users to search for customer information by email address.</p>



<p class="wp-block-paragraph">A developer might write the following JDBC code.</p>



<h3 class="wp-block-heading">A Vulnerable Java Example</h3>



<pre class="wp-block-code language-java"><code>public Customer findByEmail(
        Connection connection,
        String email
) throws SQLException {

    String sql =
        "SELECT id, email, full_name " +
        "FROM customers WHERE email = '" +
        email + "'";

    try (Statement statement =
             connection.createStatement();
         ResultSet rs =
             statement.executeQuery(sql)) {

        if (rs.next()) {
            return new Customer(
                rs.getLong("id"),
                rs.getString("email"),
                rs.getString("full_name")
            );
        }

        return null;
    }
}</code></pre>



<p class="wp-block-paragraph">The code appears straightforward.</p>



<p class="wp-block-paragraph">It creates a SQL query, executes it, and retrieves the first matching customer.</p>



<p class="wp-block-paragraph">Unfortunately, one line introduces a serious security weakness:</p>



<pre class="wp-block-code language-sql"><code>"FROM customers WHERE email = '" + email + "'"</code></pre>



<p class="wp-block-paragraph">The application inserts the supplied email address directly into the SQL command.</p>



<p class="wp-block-paragraph">That means the value becomes part of the SQL statement&#8217;s text.</p>



<p class="wp-block-paragraph">The database cannot reliably distinguish the developer&#8217;s intended query structure from SQL syntax that arrived through the user input.</p>



<h3 class="wp-block-heading">What Happens When an Attacker Changes the Input?</h3>



<p class="wp-block-paragraph">Instead of a normal email address, consider this input:</p>



<pre class="wp-block-code language-sql"><code>' OR '1'='1'</code></pre>



<p class="wp-block-paragraph">When the application concatenates it into the original query, the final SQL becomes:</p>



<pre class="wp-block-code language-sql"><code>SELECT id, email, full_name
FROM customers
WHERE email = '' OR '1'='1';</code></pre>



<p class="wp-block-paragraph">Look closely at the condition:</p>



<pre class="wp-block-code language-sql"><code>'1'='1'</code></pre>



<p class="wp-block-paragraph">This comparison always evaluates to true.</p>



<p class="wp-block-paragraph">As a result, the database may return every row instead of looking for one customer.</p>



<p class="wp-block-paragraph">The sample Java method reads only the first result, but the query itself no longer applies the intended email filter.</p>



<p class="wp-block-paragraph">If other application logic treats that result as proof of identity or authorization, the consequences could become much more serious.</p>



<p class="wp-block-paragraph">This example captures the central idea behind SQL Injection:</p>



<p class="wp-block-paragraph"><strong>The attacker turns input data into executable SQL logic.</strong></p>



<h2 class="wp-block-heading">Why Is SQL Injection Dangerous?</h2>



<p class="wp-block-paragraph">SQL Injection can affect more than a single database query.</p>



<p class="wp-block-paragraph">Its impact depends on the application&#8217;s architecture, the affected SQL statement, and the privileges assigned to the database connection.</p>



<h3 class="wp-block-heading">Exposure of Sensitive Data</h3>



<p class="wp-block-paragraph">Applications often store customer details, addresses, order histories, and other valuable information.</p>



<p class="wp-block-paragraph">A vulnerable query can expose data that users should never access.</p>



<p class="wp-block-paragraph">Even when an attacker cannot download an entire database, accessing another customer&#8217;s records creates a security and privacy problem.</p>



<h3 class="wp-block-heading">Unauthorized Data Modification</h3>



<p class="wp-block-paragraph">Some application accounts can create, update, or delete database records.</p>



<p class="wp-block-paragraph">If an attacker exploits a suitable SQL Injection vulnerability through one of these accounts, the attacker may gain opportunities to modify business data.</p>



<p class="wp-block-paragraph">For an online store, that might affect product information, customer records, or order management processes.</p>



<h3 class="wp-block-heading">Application Disruption</h3>



<p class="wp-block-paragraph">A maliciously altered query may consume excessive database resources.</p>



<p class="wp-block-paragraph">Expensive operations can reduce performance and interfere with legitimate requests.</p>



<p class="wp-block-paragraph">Under certain conditions, this behavior can contribute to a denial-of-service incident.</p>



<h3 class="wp-block-heading">Authentication and Authorization Risks</h3>



<p class="wp-block-paragraph">SQL Injection can sometimes interfere with authentication or authorization checks.</p>



<p class="wp-block-paragraph">However, not every SQL Injection vulnerability gives an attacker the ability to log in as another user.</p>



<p class="wp-block-paragraph">The actual outcome depends on how the application uses the query result and which permissions the database account holds.</p>



<h2 class="wp-block-heading">Common Types of SQL Injection</h2>



<p class="wp-block-paragraph">Security professionals commonly classify SQL Injection techniques by how attackers retrieve information or observe the application&#8217;s behavior.</p>



<h3 class="wp-block-heading">1. In-Band SQL Injection</h3>



<p class="wp-block-paragraph">In-band SQL Injection uses the same communication channel to influence the SQL query and observe its output.</p>



<p class="wp-block-paragraph">Two common forms exist.</p>



<p class="wp-block-paragraph"><strong>Error-Based SQL Injection</strong></p>



<p class="wp-block-paragraph">Attackers examine database error messages to learn about the underlying system.</p>



<p class="wp-block-paragraph">Detailed error responses may reveal query structure, table names, or other technical information.</p>



<p class="wp-block-paragraph"><strong>Union-Based SQL Injection</strong></p>



<p class="wp-block-paragraph">Attackers attempt to combine the results of different SQL queries through the <code>UNION</code> operator.</p>



<p class="wp-block-paragraph">The technique requires compatible query structures and data types.</p>



<h3 class="wp-block-heading">2. Blind SQL Injection</h3>



<p class="wp-block-paragraph">Some applications hide database errors and query output.</p>



<p class="wp-block-paragraph">That doesn&#8217;t automatically make them immune to SQL Injection.</p>



<p class="wp-block-paragraph">Attackers may still infer information by observing application behavior.</p>



<p class="wp-block-paragraph"><strong>Boolean-Based Blind SQL Injection</strong></p>



<p class="wp-block-paragraph">This technique compares how an application responds to conditions that evaluate to true or false.</p>



<p class="wp-block-paragraph"><strong>Time-Based Blind SQL Injection</strong></p>



<p class="wp-block-paragraph">This technique relies on differences in response times.</p>



<p class="wp-block-paragraph">Since network latency and server load can introduce noise, timing observations require careful interpretation.</p>



<h3 class="wp-block-heading">3. Out-of-Band SQL Injection</h3>



<p class="wp-block-paragraph">Out-of-band techniques attempt to use a separate communication channel to retrieve information.</p>



<p class="wp-block-paragraph">These attacks depend on particular database capabilities, network access, and configuration choices.</p>



<p class="wp-block-paragraph">Not every database environment supports the conditions they require.</p>



<h3 class="wp-block-heading">4. Second-Order SQL Injection</h3>



<p class="wp-block-paragraph">Second-order SQL Injection often surprises developers.</p>



<p class="wp-block-paragraph">An application first stores untrusted input in its database.</p>



<p class="wp-block-paragraph">Later, another part of the application retrieves that stored value and inserts it into a new SQL query without proper parameter binding.</p>



<p class="wp-block-paragraph">The vulnerability appears during the second operation rather than the original request.</p>



<p class="wp-block-paragraph">This illustrates an important security principle: data doesn&#8217;t automatically become trustworthy because it came from your own database.</p>



<h2 class="wp-block-heading">How to Prevent SQL Injection</h2>



<p class="wp-block-paragraph">The most effective approach starts with one rule:</p>



<p class="wp-block-paragraph"><strong>Keep SQL commands separate from data values.</strong></p>



<p class="wp-block-paragraph">OWASP recommends parameterized queries as a primary defense.</p>



<p class="wp-block-paragraph">Let&#8217;s examine what that means in practice.</p>



<h3 class="wp-block-heading">1. Use Prepared Statements and Parameterized Queries</h3>



<p class="wp-block-paragraph">Java provides the <code>PreparedStatement</code> interface through JDBC.</p>



<p class="wp-block-paragraph">Instead of embedding a value inside SQL text, we define the SQL statement and bind the value separately.</p>



<p class="wp-block-paragraph">Here&#8217;s a safer version of our customer lookup.</p>



<pre class="wp-block-code language-java"><code>public Customer findByEmail(
        Connection connection,
        String email
) throws SQLException {

    String sql = """
        SELECT id, email, full_name
        FROM customers
        WHERE email = ?
        """;

    try (PreparedStatement statement =
             connection.prepareStatement(sql)) {

        statement.setString(1, email);

        try (ResultSet rs =
                 statement.executeQuery()) {

            if (rs.next()) {
                return new Customer(
                    rs.getLong("id"),
                    rs.getString("email"),
                    rs.getString("full_name")
                );
            }

            return null;
        }
    }
}</code></pre>



<p class="wp-block-paragraph">Two details matter here.</p>



<p class="wp-block-paragraph">First, the <code>?</code> placeholder represents a data value.</p>



<p class="wp-block-paragraph">Second, <code>setString()</code> binds the supplied email address to that placeholder.</p>



<p class="wp-block-paragraph">The JDBC driver handles the value through the database parameter-binding mechanism.</p>



<p class="wp-block-paragraph">Even if someone enters:</p>



<pre class="wp-block-code language-sql"><code>' OR '1'='1'</code></pre>



<p class="wp-block-paragraph">the database treats that input as a string value to search for rather than additional SQL logic.</p>



<h3 class="wp-block-heading">Why Do Prepared Statements Work?</h3>



<p class="wp-block-paragraph">Prepared statements establish a boundary between the SQL statement&#8217;s structure and its data values.</p>



<p class="wp-block-paragraph">The application defines the query.</p>



<p class="wp-block-paragraph">The user supplies the values.</p>



<p class="wp-block-paragraph">That difference prevents data-bound input from changing the query&#8217;s intended syntax.</p>



<p class="wp-block-paragraph">It also handles legitimate values containing special characters, such as a customer&#8217;s name with an apostrophe.</p>



<p class="wp-block-paragraph">One limitation matters, though: prepared statements protect the values you bind. They cannot protect SQL fragments that you still construct through unsafe concatenation.</p>



<h3 class="wp-block-heading">2. Use Allow-Lists for Dynamic Query Structure</h3>



<p class="wp-block-paragraph">Parameterized queries work for data values, but they don&#8217;t replace SQL identifiers or keywords.</p>



<p class="wp-block-paragraph">For example, a JDBC <code>?</code> placeholder cannot represent a column name in an <code>ORDER BY</code> clause.</p>



<p class="wp-block-paragraph">Suppose your online store allows customers to sort products by price or name.</p>



<p class="wp-block-paragraph">Avoid this approach:</p>



<pre class="wp-block-code language-java"><code>String sql =
    "SELECT id, name, price FROM products ORDER BY "
    + sortBy;</code></pre>



<p class="wp-block-paragraph">Here, the application inserts untrusted input into the SQL statement&#8217;s structure.</p>



<p class="wp-block-paragraph">Instead, define which options the application supports.</p>



<pre class="wp-block-code language-java"><code>public String buildProductQuery(
        String sortBy,
        String direction
) {
    String column = switch (sortBy) {
        case "name"  -&gt; "name";
        case "price" -&gt; "price";
        default -&gt; throw new
            IllegalArgumentException(
                "Invalid sorting field"
            );
    };

    String order = switch (direction) {
        case "asc"  -&gt; "ASC";
        case "desc" -&gt; "DESC";
        default -&gt; throw new
            IllegalArgumentException(
                "Invalid sorting direction"
            );
    };

    return """
        SELECT id, name, price
        FROM products
        ORDER BY %s %s
        """.formatted(column, order);
}</code></pre>



<p class="wp-block-paragraph">The application now controls the SQL identifiers and keywords.</p>



<p class="wp-block-paragraph">Users can request only the choices the developer explicitly supports.</p>



<p class="wp-block-paragraph">Continue using prepared statements for the query&#8217;s ordinary data values.</p>



<h3 class="wp-block-heading">3. Validate Input According to Business Rules</h3>



<p class="wp-block-paragraph">Input validation helps applications reject unexpected values.</p>



<p class="wp-block-paragraph">For example, an application can require a product identifier to contain a positive integer or limit the length of a search term.</p>



<p class="wp-block-paragraph">Those checks improve reliability and reduce unnecessary processing.</p>



<p class="wp-block-paragraph">However, input validation alone doesn&#8217;t prevent SQL Injection.</p>



<p class="wp-block-paragraph">Blocking quotation marks or maintaining a blacklist of suspicious SQL keywords cannot reliably address every SQL dialect and query context.</p>



<p class="wp-block-paragraph">Use validation for business rules and parameter binding for database query safety.</p>



<h3 class="wp-block-heading">4. Apply the Principle of Least Privilege</h3>



<p class="wp-block-paragraph">Database accounts should have only the permissions their applications actually need.</p>



<p class="wp-block-paragraph">For example, a service that reads product information shouldn&#8217;t require permission to modify every customer record.</p>



<p class="wp-block-paragraph">A PostgreSQL administrator could restrict table permissions with statements such as:</p>



<pre class="wp-block-code language-sql"><code>REVOKE ALL ON TABLE products
FROM product_reader;

GRANT SELECT ON TABLE products
TO product_reader;</code></pre>



<p class="wp-block-paragraph">These statements control permissions on the specified table. A production security review must also consider role inheritance, schema permissions, and access to other database objects.</p>



<p class="wp-block-paragraph">Least privilege doesn&#8217;t remove a SQL Injection vulnerability.</p>



<p class="wp-block-paragraph">Instead, it reduces the potential damage if an attacker successfully exploits one.</p>



<p class="wp-block-paragraph">For larger architectures, consider different database roles for services with different responsibilities.</p>



<p class="wp-block-paragraph">A reporting service and an order-processing service rarely need identical database privileges.</p>



<h3 class="wp-block-heading">5. Don&#8217;t Assume Your ORM Makes Every Query Safe</h3>



<p class="wp-block-paragraph">Developers often use Hibernate, JPA, or similar frameworks to avoid writing repetitive SQL code.</p>



<p class="wp-block-paragraph">These frameworks support parameterized query APIs, but developers can still introduce vulnerabilities through unsafe query construction.</p>



<p class="wp-block-paragraph">Consider this JPQL example:</p>



<pre class="wp-block-code language-java"><code>String jpql =
    "SELECT u FROM User u WHERE u.email = '"
    + email + "'";

var query = entityManager.createQuery(
    jpql,
    User.class
);</code></pre>



<p class="wp-block-paragraph">The application still inserts untrusted input into the query text.</p>



<p class="wp-block-paragraph">A better approach uses a named parameter.</p>



<pre class="wp-block-code language-java"><code>var query = entityManager.createQuery(
    """
    SELECT u
    FROM User u
    WHERE u.email = :email
    """,
    User.class
);

query.setParameter("email", email);

List&lt;User&gt; users = query.getResultList();</code></pre>



<p class="wp-block-paragraph">Here, the ORM treats the email value as a parameter.</p>



<p class="wp-block-paragraph">Review dynamically constructed JPQL, HQL, native SQL, and sorting expressions with the same attention you give to ordinary JDBC queries.</p>



<h3 class="wp-block-heading">6. Review Stored Procedures Carefully</h3>



<p class="wp-block-paragraph">Stored procedures can help organize database logic.</p>



<p class="wp-block-paragraph">However, they don&#8217;t automatically eliminate SQL Injection.</p>



<p class="wp-block-paragraph">A procedure that constructs dynamic SQL by concatenating untrusted values can create the same vulnerability as application-level string concatenation.</p>



<p class="wp-block-paragraph">Use bound arguments when calling procedures.</p>



<p class="wp-block-paragraph">Inside the procedure, prefer static SQL or parameterized dynamic SQL.</p>



<p class="wp-block-paragraph">When a procedure needs a dynamic identifier, restrict the selection to trusted, application-approved choices.</p>



<h3 class="wp-block-heading">7. Handle Database Errors and Logs Safely</h3>



<p class="wp-block-paragraph">Database error messages help developers diagnose problems.</p>



<p class="wp-block-paragraph">But detailed SQL errors can reveal useful information to attackers.</p>



<p class="wp-block-paragraph">Production applications should avoid returning raw database exceptions to clients.</p>



<p class="wp-block-paragraph">Keep technical error details in protected logs, and provide clients with appropriate generic error responses.</p>



<p class="wp-block-paragraph">Make sure your logging strategy also protects sensitive information.</p>



<p class="wp-block-paragraph">Passwords, authentication tokens, and private customer data shouldn&#8217;t appear in ordinary diagnostic logs.</p>



<h2 class="wp-block-heading">How Can Developers Test for SQL Injection?</h2>



<p class="wp-block-paragraph">You should test only applications and environments you own or have explicit authorization to assess.</p>



<p class="wp-block-paragraph">Start with code review and repeatable automated checks.</p>



<h3 class="wp-block-heading">Review Query Construction</h3>



<p class="wp-block-paragraph">Look for places where the application builds SQL statements dynamically.</p>



<p class="wp-block-paragraph">Pay particular attention to:</p>



<ul class="wp-block-list">
<li>String concatenation inside SQL queries</li>



<li>Dynamic JDBC <code>Statement</code> execution</li>



<li>Native SQL constructed through ORM frameworks</li>



<li>Dynamic JPQL or HQL expressions</li>



<li>Stored procedures that assemble SQL text</li>



<li>User-controlled sorting fields and identifiers</li>
</ul>



<p class="wp-block-paragraph">The presence of <code>Statement</code> alone doesn&#8217;t prove a vulnerability. The risk arises when untrusted data can modify SQL syntax.</p>



<h3 class="wp-block-heading">Combine SAST and DAST</h3>



<p class="wp-block-paragraph">Static Application Security Testing (SAST) tools inspect source code for potential vulnerabilities.</p>



<p class="wp-block-paragraph">Dynamic Application Security Testing (DAST) tools examine a running application&#8217;s behavior.</p>



<p class="wp-block-paragraph">Tools such as OWASP ZAP can support an authorized testing process.</p>



<p class="wp-block-paragraph">Neither category guarantees complete coverage.</p>



<p class="wp-block-paragraph">Use automated testing alongside code review and carefully designed regression tests.</p>



<h3 class="wp-block-heading">Write a Java Regression Test</h3>



<p class="wp-block-paragraph">A simple JUnit 5 test can verify that your repository treats suspicious input as ordinary data.</p>



<pre class="wp-block-code language-java"><code>@Test
void shouldNotInterpretInputAsSql()
        throws SQLException {

    String input = "' OR '1'='1";

    Customer customer =
        repository.findByEmail(
            connection,
            input
        );

    assertNull(customer);
}</code></pre>



<p class="wp-block-paragraph">For this test, use an isolated database with known records and no customer whose email equals the supplied test string.</p>



<p class="wp-block-paragraph">You can strengthen the test by inserting a legitimate customer first and verifying that the suspicious input doesn&#8217;t return that record.</p>



<p class="wp-block-paragraph">This doesn&#8217;t replace broader security testing.</p>



<p class="wp-block-paragraph">It does, however, help prevent a previously corrected vulnerability from returning during a future code change.</p>



<h2 class="wp-block-heading">A Real-World Example: Securing an E-Commerce Product Search</h2>



<p class="wp-block-paragraph">An e-commerce application exposes many database-driven operations.</p>



<p class="wp-block-paragraph">Product searches, price filters, order histories, customer profiles, and administrative reports all deserve attention.</p>



<p class="wp-block-paragraph">Let&#8217;s implement a simple product search using Java and JDBC.</p>



<p class="wp-block-paragraph">The method accepts a keyword and a maximum price.</p>



<pre class="wp-block-code language-java"><code>public List&lt;Product&gt; searchProducts(
        Connection connection,
        String keyword,
        BigDecimal maxPrice
) throws SQLException {

    String sql = """
        SELECT id, name, price
        FROM products
        WHERE LOWER(name) LIKE LOWER(?)
          AND price &lt;= ?
        """;

    List&lt;Product&gt; products =
        new ArrayList&lt;&gt;();

    try (PreparedStatement statement =
             connection.prepareStatement(sql)) {

        statement.setString(
            1,
            "%" + keyword + "%"
        );

        statement.setBigDecimal(
            2,
            maxPrice
        );

        try (ResultSet rs =
                 statement.executeQuery()) {

            while (rs.next()) {
                products.add(
                    new Product(
                        rs.getLong("id"),
                        rs.getString("name"),
                        rs.getBigDecimal("price")
                    )
                );
            }
        }
    }

    return products;
}</code></pre>



<p class="wp-block-paragraph">This example binds both user-controlled values.</p>



<p class="wp-block-paragraph">The first parameter searches for products containing the requested keyword.</p>



<p class="wp-block-paragraph">The second parameter limits the maximum price.</p>



<p class="wp-block-paragraph">Notice that the code adds <code>%</code> characters to the Java string before binding it.</p>



<p class="wp-block-paragraph">That&#8217;s safe from SQL Injection because the application constructs a parameter value rather than adding SQL syntax to the query.</p>



<p class="wp-block-paragraph">There is one additional detail worth understanding.</p>



<p class="wp-block-paragraph">SQL <code>LIKE</code> treats <code>%</code> and <code>_</code> as wildcard characters. If your application expects literal text matching, you should handle those characters according to your database&#8217;s escaping rules.</p>



<p class="wp-block-paragraph">That&#8217;s a search-behavior concern rather than an SQL Injection vulnerability, but it demonstrates why secure code must also follow the application&#8217;s business requirements.</p>



<h2 class="wp-block-heading">Frequently Asked Questions About SQL Injection</h2>



<h3 class="wp-block-heading">Is SQL Injection still a problem in modern applications?</h3>



<p class="wp-block-paragraph">Yes. Modern frameworks provide safer database APIs, but developers can still introduce SQL Injection vulnerabilities when they concatenate untrusted values into query text.</p>



<h3 class="wp-block-heading">Does SQL Injection affect only login forms?</h3>



<p class="wp-block-paragraph">No. Search fields, filters, reports, API parameters, and other database-driven operations may expose vulnerable queries.</p>



<h3 class="wp-block-heading">Can prepared statements completely prevent SQL Injection?</h3>



<p class="wp-block-paragraph">Correctly parameterized data values cannot change the SQL statement&#8217;s structure. However, developers can still create vulnerabilities by constructing other query fragments from untrusted input.</p>



<h3 class="wp-block-heading">Does Hibernate prevent SQL Injection automatically?</h3>



<p class="wp-block-paragraph">No. Hibernate supports secure parameter binding, but unsafe dynamically constructed HQL, JPQL, or native SQL can introduce vulnerabilities.</p>



<h3 class="wp-block-heading">Should I remove special characters from user input?</h3>



<p class="wp-block-paragraph">Not as a general SQL Injection defense. Legitimate values may contain apostrophes and other special characters. Use parameter binding instead of relying on character filtering.</p>



<h3 class="wp-block-heading">Can a Web Application Firewall stop SQL Injection?</h3>



<p class="wp-block-paragraph">A WAF may detect or block some malicious requests, but it cannot guarantee protection and doesn&#8217;t fix the underlying code defect. Treat it as an additional security layer.</p>



<h3 class="wp-block-heading">What&#8217;s the difference between SQL Injection and XSS?</h3>



<p class="wp-block-paragraph">SQL Injection targets the structure of database queries. Cross-Site Scripting (XSS) targets how browsers interpret untrusted content. Both involve trust boundaries, but they affect different execution contexts.</p>



<h2 class="wp-block-heading">Final Thoughts: Make Safe Queries Your Default</h2>



<p class="wp-block-paragraph">SQL Injection becomes possible when an application allows external data to change the structure of a database query.</p>



<p class="wp-block-paragraph">You don&#8217;t need an elaborate security system to address the core problem.</p>



<p class="wp-block-paragraph">Start by writing parameterized queries. Use explicit allow-lists when users need to influence query structure. Limit database privileges and include security checks in your development workflow.</p>



<p class="wp-block-paragraph">If you&#8217;re writing Java, make <code>PreparedStatement</code> your default choice for JDBC queries that include variable data.</p>



<p class="wp-block-paragraph">If you&#8217;re using an ORM, learn how its parameter-binding APIs work and avoid unsafe dynamic query construction.</p>



<p class="wp-block-paragraph">Most importantly, keep one principle in mind:</p>



<p class="wp-block-paragraph"><strong>Your application defines the SQL. Your users provide the data.</strong></p>



<p class="wp-block-paragraph">That separation is the foundation of SQL Injection prevention.</p>



<h2 class="wp-block-heading">References and Further Reading</h2>



<ol start="1" class="wp-block-list">
<li>OWASP — SQL Injection Prevention Cheat Sheet<br><a href="https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html">https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html</a></li>



<li>OWASP — Top 10:2025, A05 Injection<br><a href="https://top10.owasp.org/2025/A05_2025-Injection/">https://top10.owasp.org/2025/A05_2025-Injection/</a></li>



<li>OWASP — Web Security Testing Guide<br><a href="https://owasp.org/www-project-web-security-testing-guide/">https://owasp.org/www-project-web-security-testing-guide/</a></li>



<li>Oracle — Using Prepared Statements<br><a href="https://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html">https://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html</a></li>
</ol>



<p class="wp-block-paragraph"></p>
<p><a href="https://alikarahisar.com/blog/sql-injection-a-friendly-guide-to-understanding-the-threat/">SQL Injection: A Friendly Guide to Understanding the Threat</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://alikarahisar.com/blog/sql-injection-a-friendly-guide-to-understanding-the-threat/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SQL Injection Nedir? Saldırıyı Anlamak ve Önlemek İçin Pratik Rehber</title>
		<link>https://alikarahisar.com/blog/sql-injection-nedir/</link>
					<comments>https://alikarahisar.com/blog/sql-injection-nedir/#respond</comments>
		
		<dc:creator><![CDATA[Ali Karahisar]]></dc:creator>
		<pubDate>Thu, 29 Oct 2026 07:29:14 +0000</pubDate>
				<category><![CDATA[Siber Güvenlik]]></category>
		<category><![CDATA[Yazılım]]></category>
		<category><![CDATA[Blind SQL Injection]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[OWASP SQL Injection]]></category>
		<category><![CDATA[Parametreli SQL sorguları]]></category>
		<category><![CDATA[PreparedStatement Java]]></category>
		<category><![CDATA[SQL enjeksiyonu]]></category>
		<category><![CDATA[SQL güvenlik açığı]]></category>
		<category><![CDATA[SQL Injection güvenlik testi]]></category>
		<category><![CDATA[SQL Injection nasıl çalışır]]></category>
		<category><![CDATA[SQL Injection nasıl önlenir]]></category>
		<category><![CDATA[SQL Injection nedir]]></category>
		<category><![CDATA[SQL Injection örnekleri]]></category>
		<category><![CDATA[SQL Injection saldırısı]]></category>
		<guid isPermaLink="false">https://alikarahisar.com/blog/?p=113</guid>

					<description><![CDATA[<p>Bir web uygulamasının giriş ekranına kullanıcı adınızı yazdığınızı düşünün. Uygulama bu bilgiyi veritabanına gönderiyor ve hesabınızı arıyor. Buraya kadar her şey normal. Peki, kullanıcı adı alanına normal bir isim yerine SQL komutu yazarsanız ne olur? Uygulama bu girdiyi doğrudan SQL sorgusuna ekliyorsa veritabanı beklediğinizden farklı bir komut çalıştırabilir. Böylece basit bir metin alanı, veritabanı güvenliğini [&#8230;]</p>
<p><a href="https://alikarahisar.com/blog/sql-injection-nedir/">SQL Injection Nedir? Saldırıyı Anlamak ve Önlemek İçin Pratik Rehber</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Bir web uygulamasının giriş ekranına kullanıcı adınızı yazdığınızı düşünün. Uygulama bu bilgiyi veritabanına gönderiyor ve hesabınızı arıyor. Buraya kadar her şey normal.</p>



<p class="wp-block-paragraph">Peki, kullanıcı adı alanına normal bir isim yerine SQL komutu yazarsanız ne olur?</p>



<span id="more-113"></span>



<p class="wp-block-paragraph">Uygulama bu girdiyi doğrudan SQL sorgusuna ekliyorsa veritabanı beklediğinizden farklı bir komut çalıştırabilir. Böylece basit bir metin alanı, veritabanı güvenliğini tehdit eden bir noktaya dönüşür.</p>



<p class="wp-block-paragraph">Bu güvenlik açığına <strong>SQL Injection (SQL enjeksiyonu)</strong> adını veriyoruz.</p>



<p class="wp-block-paragraph">SQL Injection yalnızca eski web uygulamalarının sorunu değil. Modern Java uygulamaları, REST API&#8217;ler, mikroservisler ve ORM kullanan sistemler de hatalı sorgu oluşturduklarında aynı riskle karşılaşır.</p>



<p class="wp-block-paragraph">Bu rehberde SQL Injection&#8217;ın nasıl çalıştığını, hangi hataların güvenlik açığı oluşturduğunu ve Java ile güvenli veritabanı sorgularını nasıl geliştirebileceğinizi inceleyeceğiz.</p>



<h2 class="wp-block-heading">SQL Injection Nedir?</h2>



<p class="wp-block-paragraph">SQL Injection, saldırganın uygulamanın SQL sorgusuna kendi girdisi üzerinden müdahale etmesini sağlayan bir güvenlik açığıdır.</p>



<p class="wp-block-paragraph">Sorunun temelinde SQL komutları ile kullanıcı verilerinin birbirine karışması yatar.</p>



<p class="wp-block-paragraph">Bir uygulama kullanıcı girdisini SQL sorgusunun metnine doğrudan eklediğinde, saldırgan sorgunun mantığını değiştirebilir.</p>



<p class="wp-block-paragraph">Bu durum saldırgana uygulamanın veritabanı izinleri ve sorgunun yapısına bağlı olarak şu fırsatları verebilir:</p>



<ul class="wp-block-list">
<li>Normalde erişemeyeceği verilere ulaşmak</li>



<li>Kullanıcı hesapları ve müşteri bilgilerini okumak</li>



<li>Yetkisiz veri değişiklikleri yapmak</li>



<li>Kayıtları silmek veya bozmak</li>



<li>Kimlik doğrulama kontrollerini aşmak</li>



<li>Veritabanı hizmetinin çalışmasını olumsuz etkilemek</li>
</ul>



<p class="wp-block-paragraph">Bazı veritabanı özellikleri ve yüksek yetkili hesaplar, saldırının etkisini veritabanının dışına da taşıyabilir.</p>



<p class="wp-block-paragraph">SQL Injection, OWASP Top 10:2025 listesindeki <strong>A05:2025 – Injection</strong> kategorisinin kapsamına girer.</p>



<p class="wp-block-paragraph">Burada önemli nokta şu: SQL dili tek başına güvenlik açığı oluşturmaz. Açığı, uygulamanın güvenilmeyen verileri SQL sözdiziminin parçası hâline getirmesi oluşturur.</p>



<h2 class="wp-block-heading">SQL Injection Nasıl Çalışır?</h2>



<p class="wp-block-paragraph">Bir e-ticaret uygulamasında kullanıcıların e-posta adresleri üzerinden hesap bilgilerini aradığımızı düşünelim.</p>



<p class="wp-block-paragraph">Normal koşullarda uygulama şu sorguyu çalıştırır:</p>



<pre class="wp-block-code language-sql"><code>SELECT id, email, full_name
FROM customers
WHERE email = 'ali@example.com';</code></pre>



<p class="wp-block-paragraph">Veritabanı yalnızca ilgili e-posta adresini taşıyan kaydı döndürür.</p>



<p class="wp-block-paragraph">Şimdi Java tarafında SQL sorgusunu nasıl oluşturduğumuza bakalım.</p>



<h3 class="wp-block-heading">Güvensiz Java Kod Örneği</h3>



<pre class="wp-block-code language-java"><code>public Customer findByEmail(
        Connection connection,
        String email
) throws SQLException {

    String sql = "SELECT id, email, full_name " +
                 "FROM customers WHERE email = '" +
                 email + "'";

    try (Statement statement =
             connection.createStatement();
         ResultSet rs = statement.executeQuery(sql)) {

        if (rs.next()) {
            return new Customer(
                rs.getLong("id"),
                rs.getString("email"),
                rs.getString("full_name")
            );
        }

        return null;
    }
}</code></pre>



<p class="wp-block-paragraph">İlk bakışta kod oldukça basit. Geliştirici e-posta adresini SQL sorgusuna ekliyor ve sorguyu çalıştırıyor.</p>



<p class="wp-block-paragraph">Ancak şu satır ciddi bir güvenlik problemi yaratıyor:</p>



<pre class="wp-block-code language-sql"><code>"FROM customers WHERE email = '" + email + "'"</code></pre>



<p class="wp-block-paragraph">Uygulama, <code>email</code> değişkeninin içeriğini SQL komutunun bir parçası hâline getiriyor.</p>



<p class="wp-block-paragraph">Geliştirici burada SQL&#8217;in nerede bittiğini ve kullanıcı verisinin nerede başladığını veritabanına güvenilir şekilde anlatmıyor.</p>



<h3 class="wp-block-heading">Saldırgan Sorguyu Nasıl Değiştirir?</h3>



<p class="wp-block-paragraph">Saldırganın e-posta alanına şu ifadeyi yazdığını düşünelim:</p>



<pre class="wp-block-code language-sql"><code>' OR '1'='1'</code></pre>



<p class="wp-block-paragraph">Uygulama bu ifadeyi sorguya doğrudan eklerse ortaya şu SQL çıkar:</p>



<pre class="wp-block-code language-sql"><code>SELECT id, email, full_name
FROM customers
WHERE email = '' OR '1'='1';</code></pre>



<p class="wp-block-paragraph">Sorgunun mantığı artık değişti.</p>



<p class="wp-block-paragraph"><code>'1'='1'</code> ifadesi her zaman doğru sonuç verir. Dolayısıyla <code>WHERE</code> koşulu bütün satırlar için doğru olur.</p>



<p class="wp-block-paragraph">Veritabanı sorgusu tek bir müşteri yerine birden fazla müşteriyi döndürebilir.</p>



<p class="wp-block-paragraph">Örnekteki Java metodu yalnızca ilk kaydı okuyacak olsa da saldırgan arama sonucunun kapsamını değiştirmiş olur. Uygulamanın geri kalan mantığı bu davranışı hesap erişimi veya yetkilendirme için kullanıyorsa daha ciddi sorunlar ortaya çıkabilir.</p>



<p class="wp-block-paragraph">Bu örnek, SQL Injection&#8217;ın temel mekanizmasını gösterir: <strong>Saldırgan veri göndermek yerine sorgunun mantığını değiştirir.</strong></p>



<h2 class="wp-block-heading">SQL Injection Neden Tehlikelidir?</h2>



<p class="wp-block-paragraph">Saldırının etkisini yalnızca sorgu sonucunda kaç kayıt gördüğümüzle ölçemeyiz.</p>



<p class="wp-block-paragraph">Veritabanı kullanıcısının yetkileri, uygulamanın işlevleri ve verinin niteliği de sonuçları belirler.</p>



<h3 class="wp-block-heading">Veri Gizliliğinin Kaybı</h3>



<p class="wp-block-paragraph">Bir uygulama müşteri adreslerini, e-posta bilgilerini veya finansal kayıtları saklıyor olabilir.</p>



<p class="wp-block-paragraph">SQL Injection açığı, saldırganın bu verilere yetkisiz erişim elde etmesine yol açabilir.</p>



<p class="wp-block-paragraph">Güvenlik açısından sorun yalnızca verinin dışarı çıkması değildir. Uygulamanın normalde göstermediği bilgilerin başka bir kullanıcıya ulaşması da gizlilik ihlalidir.</p>



<h3 class="wp-block-heading">Veri Bütünlüğünün Bozulması</h3>



<p class="wp-block-paragraph">Yeterli veritabanı yetkisine sahip bir uygulama üzerinden saldırgan bazı kayıtları değiştirebilir veya silebilir.</p>



<p class="wp-block-paragraph">Örneğin ürün fiyatları, sipariş durumları ve müşteri hesap bilgileri saldırgan için hedef oluşturabilir.</p>



<p class="wp-block-paragraph">Bu tür değişiklikler yalnızca teknik hata yaratmaz. İş süreçlerini ve raporların doğruluğunu da etkiler.</p>



<h3 class="wp-block-heading">Hizmet Kesintileri</h3>



<p class="wp-block-paragraph">Bazı SQL sorguları çok fazla işlemci, bellek veya disk kaynağı tüketebilir.</p>



<p class="wp-block-paragraph">Saldırganın sorgu davranışını değiştirmesi, veritabanının performansını düşürebilir. Ağır durumlarda uygulama hizmet veremez hâle gelebilir.</p>



<h3 class="wp-block-heading">Yetkilendirme Sorunları</h3>



<p class="wp-block-paragraph">SQL Injection, kimlik doğrulama ve yetkilendirme mekanizmalarını etkileyebilir.</p>



<p class="wp-block-paragraph">Ancak her SQL Injection açığı otomatik olarak giriş ekranını aşma imkânı vermez.</p>



<p class="wp-block-paragraph">Saldırganın hangi işlemleri gerçekleştirebileceğini sorgunun konumu, uygulama mantığı ve veritabanı yetkileri belirler.</p>



<h2 class="wp-block-heading">SQL Injection Türleri</h2>



<p class="wp-block-paragraph">Güvenlik uzmanları SQL Injection saldırılarını çoğunlukla uygulamanın veritabanından nasıl yanıt aldığına göre sınıflandırır.</p>



<h3 class="wp-block-heading">1. In-Band SQL Injection</h3>



<p class="wp-block-paragraph">Bu yöntemde saldırgan aynı iletişim kanalı üzerinden hem sorguyu etkiler hem de sonucu görür.</p>



<p class="wp-block-paragraph">İki yaygın alt türü bulunur.</p>



<p class="wp-block-paragraph"><strong>Error-Based SQL Injection</strong></p>



<p class="wp-block-paragraph">Saldırgan, veritabanının ürettiği hata mesajlarından teknik bilgi elde etmeye çalışır.</p>



<p class="wp-block-paragraph">Hata mesajları bazen tablo isimleri, sütunlar veya SQL sözdizimi hakkında ipuçları verebilir.</p>



<p class="wp-block-paragraph"><strong>Union-Based SQL Injection</strong></p>



<p class="wp-block-paragraph">Saldırgan, uygun koşullarda <code>UNION</code> operatörünü kullanarak farklı sorguların sonuçlarını birleştirmeye çalışır.</p>



<p class="wp-block-paragraph">Bu yöntem belirli sorgu yapısı ve veri tipi uyumluluğu koşullarını gerektirir.</p>



<h3 class="wp-block-heading">2. Blind SQL Injection</h3>



<p class="wp-block-paragraph">Bazı uygulamalar sorgu sonucunu veya veritabanı hatalarını doğrudan göstermez.</p>



<p class="wp-block-paragraph">Bu durum SQL Injection riskini ortadan kaldırmaz.</p>



<p class="wp-block-paragraph">Saldırgan, uygulamanın davranışındaki küçük farkları gözlemleyerek bilgi elde etmeye çalışabilir.</p>



<p class="wp-block-paragraph"><strong>Boolean-Based Blind SQL Injection</strong></p>



<p class="wp-block-paragraph">Saldırgan doğru ve yanlış koşullara karşılık uygulamanın farklı tepkiler verip vermediğini inceler.</p>



<p class="wp-block-paragraph"><strong>Time-Based Blind SQL Injection</strong></p>



<p class="wp-block-paragraph">Saldırgan veritabanının yanıt süresindeki farklılıklardan çıkarım yapmaya çalışır.</p>



<p class="wp-block-paragraph">Ancak ağ gecikmeleri ve sistem yükü, zaman ölçümlerini yanıltabilir.</p>



<h3 class="wp-block-heading">3. Out-of-Band SQL Injection</h3>



<p class="wp-block-paragraph">Bazı senaryolarda saldırgan, veritabanının farklı bir iletişim kanalı üzerinden dış sistemlerle etkileşim kurmasını hedefler.</p>



<p class="wp-block-paragraph">Bu yaklaşım özel veritabanı özellikleri, yapılandırmalar ve ağ erişim koşulları gerektirir.</p>



<h3 class="wp-block-heading">4. Second-Order SQL Injection</h3>



<p class="wp-block-paragraph">Bu türü özellikle gözden kaçırmak kolaydır.</p>



<p class="wp-block-paragraph">Uygulama saldırganın girdisini önce veritabanına kaydeder. Daha sonra başka bir işlem bu veriyi okuyup güvensiz biçimde yeni bir SQL sorgusuna ekler.</p>



<p class="wp-block-paragraph">Dolayısıyla güvenlik açığı ilk veri girişinde değil, verinin sonraki kullanımında ortaya çıkar.</p>



<p class="wp-block-paragraph">Bu nedenle geliştirici yalnızca HTTP isteklerini değil, veritabanından, dosyalardan ve diğer servislerden gelen verileri de sorgu oluştururken güvenilmeyen girdi olarak ele almalıdır.</p>



<h2 class="wp-block-heading">SQL Injection Nasıl Önlenir?</h2>



<p class="wp-block-paragraph">SQL Injection&#8217;a karşı en güçlü temel savunma, SQL komutlarının yapısını kullanıcı verilerinden ayrı tutmaktır.</p>



<p class="wp-block-paragraph">OWASP, veri değerleri için parametreli sorguları temel savunma olarak önerir.</p>



<h3 class="wp-block-heading">1. PreparedStatement ve Parametreli Sorgular Kullanın</h3>



<p class="wp-block-paragraph">Java JDBC API, <code>PreparedStatement</code> sınıfıyla parametreli sorgular oluşturmanıza imkân verir.</p>



<p class="wp-block-paragraph">Önceki güvensiz örneği yeniden yazalım.</p>



<pre class="wp-block-code language-java"><code>public Customer findByEmail(
        Connection connection,
        String email
) throws SQLException {

    String sql = """
        SELECT id, email, full_name
        FROM customers
        WHERE email = ?
        """;

    try (PreparedStatement statement =
             connection.prepareStatement(sql)) {

        statement.setString(1, email);

        try (ResultSet rs =
                 statement.executeQuery()) {

            if (rs.next()) {
                return new Customer(
                    rs.getLong("id"),
                    rs.getString("email"),
                    rs.getString("full_name")
                );
            }

            return null;
        }
    }
}</code></pre>



<p class="wp-block-paragraph">Burada iki önemli değişiklik yaptık.</p>



<p class="wp-block-paragraph">İlk olarak kullanıcı girdisini SQL metnine eklemek yerine <code>?</code> yer tutucusunu kullandık.</p>



<p class="wp-block-paragraph">İkinci olarak <code>setString()</code> metoduyla parametre değerini sorguya bağladık.</p>



<p class="wp-block-paragraph">JDBC sürücüsü, parametre bağlama mekanizması üzerinden SQL yapısıyla veri değerini ayrı ele alır.</p>



<p class="wp-block-paragraph">Artık kullanıcı şu ifadeyi gönderse bile:</p>



<pre class="wp-block-code language-sql"><code>' OR '1'='1'</code></pre>



<p class="wp-block-paragraph">Veritabanı bunu sorgunun mantığını değiştiren bir SQL ifadesi olarak değil, <code>email</code> sütununda aranacak bir değer olarak değerlendirir.</p>



<p class="wp-block-paragraph"><strong>PreparedStatement neden güvenlidir?</strong></p>



<p class="wp-block-paragraph">Çünkü SQL&#8217;in komut yapısı ile dışarıdan gelen veri arasında açık bir sınır kurar.</p>



<p class="wp-block-paragraph">Tek başına kullanıcı girdisindeki özel karakterleri yasaklamamız gerekmez.</p>



<p class="wp-block-paragraph">Örneğin <code>O'Connor</code> gibi tek tırnak içeren gerçek isimler de güvenli biçimde işlenebilir.</p>



<p class="wp-block-paragraph">Ancak geliştirici SQL yapısını kullanıcı girdisiyle birleştirmeye devam ederse yalnızca sorgunun başka bir bölümünde PreparedStatement kullanması yeterli olmaz.</p>



<h3 class="wp-block-heading">2. Dinamik SQL Yapıları İçin Allowlist Kullanın</h3>



<p class="wp-block-paragraph">Parametreli sorguların önemli bir sınırı bulunur.</p>



<p class="wp-block-paragraph"><code>?</code> yer tutucusu tablo ismi, sütun ismi veya <code>ASC</code> ve <code>DESC</code> gibi SQL anahtar kelimeleri yerine geçmez.</p>



<p class="wp-block-paragraph">Bir e-ticaret uygulamasında kullanıcılar ürünleri fiyat veya isim üzerinden sıralamak isteyebilir.</p>



<p class="wp-block-paragraph">Şu yaklaşım güvenli değildir:</p>



<pre class="wp-block-code language-sql"><code>String sql =
    "SELECT id, name, price FROM products ORDER BY "
    + sortBy;</code></pre>



<p class="wp-block-paragraph">Çünkü uygulama dışarıdan gelen <code>sortBy</code> değerini doğrudan SQL yapısına ekler.</p>



<p class="wp-block-paragraph">Bunun yerine izin verdiğimiz seçenekleri kodda tanımlayalım.</p>



<pre class="wp-block-code language-java"><code>public String createProductQuery(
        String sortBy,
        String direction
) {
    String column = switch (sortBy) {
        case "price" -&gt; "price";
        case "name"  -&gt; "name";
        default -&gt; throw new
            IllegalArgumentException(
                "Unsupported sort field"
            );
    };

    String order = switch (direction) {
        case "asc"  -&gt; "ASC";
        case "desc" -&gt; "DESC";
        default -&gt; throw new
            IllegalArgumentException(
                "Unsupported direction"
            );
    };

    return """
        SELECT id, name, price
        FROM products
        ORDER BY %s %s
        """.formatted(column, order);
}</code></pre>



<p class="wp-block-paragraph">Burada kullanıcı yalnızca önceden tanımladığımız seçenekler arasından seçim yapabilir.</p>



<p class="wp-block-paragraph">Uygulama SQL sorgusuna doğrudan kullanıcı girdisini değil, kendi belirlediği güvenilir SQL ifadelerini ekler.</p>



<p class="wp-block-paragraph">Dinamik yapıları bu şekilde sınırlandırırken diğer veri değerleri için yine parametreli sorgular kullanmalıyız.</p>



<h3 class="wp-block-heading">3. Girdi Doğrulama Uygulayın</h3>



<p class="wp-block-paragraph">Bir e-posta alanı e-posta biçimini, bir ürün kimliği ise beklenen veri tipini taşımalıdır.</p>



<p class="wp-block-paragraph">Girdi doğrulama uygulamanın beklenmedik verilerle karşılaşmasını azaltır.</p>



<p class="wp-block-paragraph">Ancak doğrulama SQL Injection&#8217;a karşı tek başına yeterli değildir.</p>



<p class="wp-block-paragraph">Örneğin sadece tek tırnak karakterini engellemek güvenilir bir savunma oluşturmaz.</p>



<p class="wp-block-paragraph">Farklı SQL sözdizimleri, karakter dönüşümleri ve sorgu bağlamları bu tür filtreleri yetersiz bırakabilir.</p>



<p class="wp-block-paragraph">Doğru yaklaşım şudur:</p>



<ul class="wp-block-list">
<li>Girdi doğrulamayı iş kuralları için kullanın.</li>



<li>Parametreli sorgularla SQL yapısını koruyun.</li>



<li>Dinamik SQL parçalarını allowlist ile sınırlandırın.</li>
</ul>



<h3 class="wp-block-heading">4. Veritabanında En Az Ayrıcalık İlkesini Uygulayın</h3>



<p class="wp-block-paragraph">Bir uygulamanın yalnızca müşteri listesini okuması gerekiyorsa veritabanı hesabına tüm tabloları değiştirme yetkisi vermemelisiniz.</p>



<p class="wp-block-paragraph">Örneğin PostgreSQL üzerinde yalnızca ürün okuma görevi olan bir hesap için gerekli erişimi sınırlayabilirsiniz.</p>



<pre class="wp-block-code language-sql"><code>REVOKE ALL ON TABLE products
FROM product_reader;

GRANT SELECT ON TABLE products
TO product_reader;</code></pre>



<p class="wp-block-paragraph">Bu örnek ilgili tablo üzerindeki yetkileri sınırlar. Gerçek sistemde rol üyeliklerini, şema erişimini, varsayılan yetkileri ve diğer tabloları da kontrol etmelisiniz.</p>



<p class="wp-block-paragraph">En az ayrıcalık ilkesi SQL Injection açığını doğrudan engellemez.</p>



<p class="wp-block-paragraph">Ancak saldırgan bir açık bulsa bile veritabanı hesabının yapabileceği işlemleri sınırlar.</p>



<h3 class="wp-block-heading">5. ORM Kullanırken Dinamik Sorgulara Dikkat Edin</h3>



<p class="wp-block-paragraph">Hibernate veya JPA kullanmak SQL Injection riskini otomatik olarak ortadan kaldırmaz.</p>



<p class="wp-block-paragraph">Örneğin şu JPQL oluşturma biçimi risklidir:</p>



<pre class="wp-block-code language-sql"><code>String jpql =
    "SELECT u FROM User u WHERE u.email = '"
    + email + "'";

var query = entityManager.createQuery(
    jpql,
    User.class
);</code></pre>



<p class="wp-block-paragraph">Güvenli yaklaşımda parametre bağlamalıyız:</p>



<pre class="wp-block-code language-java"><code>var query = entityManager.createQuery(
    """
    SELECT u
    FROM User u
    WHERE u.email = :email
    """,
    User.class
);

query.setParameter("email", email);

List&lt;User&gt; users = query.getResultList();</code></pre>



<p class="wp-block-paragraph">ORM araçları geliştiricinin işini kolaylaştırır. Ancak sorgu oluşturma kararının güvenliği yine geliştiricinin sorumluluğundadır.</p>



<p class="wp-block-paragraph">Özellikle native SQL, dinamik JPQL/HQL ve elle oluşturduğunuz sıralama koşullarını dikkatle incelemelisiniz.</p>



<h3 class="wp-block-heading">6. Stored Procedure Kullanımını da Denetleyin</h3>



<p class="wp-block-paragraph">Stored procedure kullanımı SQL Injection riskini tek başına ortadan kaldırmaz.</p>



<p class="wp-block-paragraph">Bir procedure içinde kullanıcı verilerini birleştirerek dinamik SQL oluşturursanız aynı güvenlik açığını yeniden yaratabilirsiniz.</p>



<p class="wp-block-paragraph">Procedure çağrısında parametre bağlamak önemlidir. Ancak procedure içindeki dinamik sorgular için de aynı güvenlik kuralını uygulamalısınız.</p>



<p class="wp-block-paragraph">Statik SQL tercih edin. Dinamik SQL gerektiğinde veri parametrelerini bağlayın ve SQL yapılarını güvenilir seçeneklerle sınırlandırın.</p>



<h3 class="wp-block-heading">7. Hata Mesajlarını ve Kayıtları Kontrol Edin</h3>



<p class="wp-block-paragraph">Geliştirme sırasında ayrıntılı SQL hataları sorunları çözmenize yardımcı olur.</p>



<p class="wp-block-paragraph">Ancak canlı ortamda ham veritabanı hatalarını istemciye göndermek gereksiz bilgi açığa çıkarabilir.</p>



<p class="wp-block-paragraph">Uygulama, kullanıcıya genel bir hata yanıtı vermeli ve teknik ayrıntıları yetkili ekiplerin erişebileceği güvenli log sisteminde tutmalıdır.</p>



<p class="wp-block-paragraph">Log kayıtlarına parola, erişim belirteci veya hassas kişisel veri eklemeyin.</p>



<p class="wp-block-paragraph">Ayrıca veritabanı kullanıcısının izinlerini ve uygulamanın hata yönetimini düzenli olarak inceleyin.</p>



<h2 class="wp-block-heading">SQL Injection Açıklarını Nasıl Test Edebilirsiniz?</h2>



<p class="wp-block-paragraph">SQL Injection kontrollerini yalnızca sahibi olduğunuz veya açıkça test izni aldığınız sistemlerde gerçekleştirin.</p>



<p class="wp-block-paragraph">Güvenli bir geliştirme ortamında aşağıdaki yaklaşımı kullanabilirsiniz.</p>



<h3 class="wp-block-heading">Kod İncelemesi</h3>



<p class="wp-block-paragraph">Öncelikle SQL sorgularını oluşturan kodu inceleyin.</p>



<p class="wp-block-paragraph">Özellikle şu kalıplara dikkat edin:</p>



<ul class="wp-block-list">
<li>SQL metniyle kullanıcı girdisinin birleştirilmesi</li>



<li><code>Statement</code> ile dinamik sorgu çalıştırma</li>



<li>Native SQL ve dinamik JPQL/HQL oluşturma</li>



<li>Stored procedure içinde dinamik SQL kullanımı</li>



<li><code>ORDER BY</code> ve tablo isimleri gibi yapısal alanlarda kullanıcı girdisi kullanımı</li>
</ul>



<p class="wp-block-paragraph">Her <code>Statement</code> kullanımı otomatik olarak açık anlamına gelmez. Asıl risk, güvenilmeyen girdinin SQL komutunun yapısını değiştirebilmesidir.</p>



<h3 class="wp-block-heading">Otomatik Güvenlik Testleri</h3>



<p class="wp-block-paragraph">Statik uygulama güvenliği testleri (SAST), kaynak kodda şüpheli sorgu oluşturma kalıplarını bulmanıza yardımcı olabilir.</p>



<p class="wp-block-paragraph">Dinamik uygulama güvenliği testleri (DAST), çalışan uygulamanın girdilere verdiği tepkileri analiz eder.</p>



<p class="wp-block-paragraph">OWASP ZAP ve uygun yapılandırılmış güvenlik test araçları, yetkili test süreçlerinde yardımcı olabilir.</p>



<p class="wp-block-paragraph">Bir aracın herhangi bir bulgu üretmemesi uygulamanın kesinlikle güvenli olduğu anlamına gelmez.</p>



<h3 class="wp-block-heading">Java ile Basit Bir Regresyon Testi</h3>



<p class="wp-block-paragraph">Parametreli sorgunun beklendiği gibi davrandığını birim veya entegrasyon testinde de kontrol edebilirsiniz.</p>



<p class="wp-block-paragraph">Örneğin aşağıdaki JUnit 5 testi, depo katmanının şüpheli bir girdiyi geçerli müşteri kaydı gibi yorumlamamasını bekler.</p>



<pre class="wp-block-code language-java"><code>@Test
void shouldTreatSqlPayloadAsPlainData()
        throws SQLException {

    String input = "' OR '1'='1";

    Customer result =
        repository.findByEmail(
            connection,
            input
        );

    assertNull(result);
}</code></pre>



<p class="wp-block-paragraph">Bu testi çalıştırırken izole test veritabanınızda ilgili girdiyi e-posta değeri olarak taşıyan bir kayıt bulunmadığından emin olun.</p>



<p class="wp-block-paragraph">Testin gerçek değerini artırmak için veritabanını başlangıç verileriyle kurun, bilinen bir müşteri kaydı ekleyin ve sorgunun beklenmedik kayıt döndürmediğini doğrulayın.</p>



<p class="wp-block-paragraph">Tek bir test bütün SQL Injection türlerini kapsamaz. Ancak daha önce düzelttiğiniz bir açığın tekrar ortaya çıkmasını yakalayabilir.</p>



<h2 class="wp-block-heading">Bir E-Ticaret Uygulamasında SQL Injection Güvenliği</h2>



<p class="wp-block-paragraph">Bir e-ticaret sisteminin yalnızca giriş ekranını koruduğunu varsayalım.</p>



<p class="wp-block-paragraph">Bu yeterli değildir.</p>



<p class="wp-block-paragraph">Ürün arama, fiyat filtreleme, müşteri hesapları, sipariş geçmişi, kampanya sorguları ve yönetim paneli aynı güvenlik prensiplerine ihtiyaç duyar.</p>



<p class="wp-block-paragraph">Örneğin ürün arama sorgusunu şöyle oluşturabiliriz:</p>



<pre class="wp-block-code language-java"><code>public List&lt;Product&gt; searchProducts(
        Connection connection,
        String keyword,
        BigDecimal maxPrice
) throws SQLException {

    String sql = """
        SELECT id, name, price
        FROM products
        WHERE LOWER(name) LIKE LOWER(?)
          AND price &lt;= ?
        """;

    List&lt;Product&gt; products =
        new ArrayList&lt;&gt;();

    try (PreparedStatement statement =
             connection.prepareStatement(sql)) {

        statement.setString(
            1,
            "%" + keyword + "%"
        );

        statement.setBigDecimal(
            2,
            maxPrice
        );

        try (ResultSet rs =
                 statement.executeQuery()) {

            while (rs.next()) {
                products.add(
                    new Product(
                        rs.getLong("id"),
                        rs.getString("name"),
                        rs.getBigDecimal("price")
                    )
                );
            }
        }
    }

    return products;
}</code></pre>



<p class="wp-block-paragraph">Bu örnekte iki farklı parametre bağladık.</p>



<p class="wp-block-paragraph">İlk parametre ürün isminde arama yapıyor. İkinci parametre maksimum fiyatı belirliyor.</p>



<p class="wp-block-paragraph"><code>keyword</code> değerini Java tarafında <code>%</code> karakterleriyle birleştiriyoruz. Ancak bu birleşim SQL sorgusunun yapısını değil, parametre değerini değiştiriyor.</p>



<p class="wp-block-paragraph">Bu nedenle sorgu SQL Injection&#8217;a karşı güvenli bir sınır koruyor.</p>



<p class="wp-block-paragraph">Burada küçük bir ayrıntı daha var: Kullanıcı <code>%</code> veya <code>_</code> karakterlerini arama metnine eklerse <code>LIKE</code> operatörü bu karakterlere özel anlam yükleyebilir.</p>



<p class="wp-block-paragraph">Bu davranış SQL Injection değildir. Ancak uygulama düz metin araması bekliyorsa geliştirici joker karakterleri ayrıca ele almalıdır.</p>



<p class="wp-block-paragraph">Güvenli yazılım geliştirme yalnızca SQL Injection&#8217;ı durdurmakla bitmez. Uygulamanın iş kurallarını doğru uygulamasını da gerektirir.</p>



<h2 class="wp-block-heading">SQL Injection Hakkında Sık Sorulan Sorular</h2>



<h3 class="wp-block-heading">SQL Injection sadece giriş ekranlarında mı ortaya çıkar?</h3>



<p class="wp-block-paragraph">Hayır. Arama alanları, filtreler, raporlama ekranları, API parametreleri ve veritabanına sorgu gönderen diğer işlemler SQL Injection riski taşıyabilir.</p>



<h3 class="wp-block-heading">PreparedStatement SQL Injection&#8217;ı tamamen engeller mi?</h3>



<p class="wp-block-paragraph">PreparedStatement, doğru parametre bağlama kullanımıyla sorgunun veri değerlerinden kaynaklanan SQL Injection riskini önler. Ancak kullanıcı girdisini SQL yapısına başka bir noktada eklemek yeni bir açık oluşturabilir.</p>



<h3 class="wp-block-heading">Hibernate veya JPA kullanırsam güvende olur muyum?</h3>



<p class="wp-block-paragraph">ORM araçları parametreli sorgu kullanımını kolaylaştırır. Ancak dinamik sorguları güvensiz biçimde oluşturursanız SQL Injection açığı yine ortaya çıkabilir.</p>



<h3 class="wp-block-heading">SQL Injection ve XSS aynı şey mi?</h3>



<p class="wp-block-paragraph">Hayır. SQL Injection veritabanı sorgularının yapısını hedefler. Cross-Site Scripting (XSS) ise tarayıcının işlediği içerik üzerinden istemci tarafındaki güven sınırlarını hedefler.</p>



<h3 class="wp-block-heading">SQL Injection önlemek için kullanıcı girdisindeki özel karakterleri engellemeli miyim?</h3>



<p class="wp-block-paragraph">Hayır. Genel bir özel karakter yasağı güvenilir çözüm sunmaz ve geçerli kullanıcı verilerini de reddedebilir. Parametreli sorgular, iş kurallarına uygun doğrulama ve allowlist yaklaşımı daha doğru bir savunma oluşturur.</p>



<h3 class="wp-block-heading">Web Application Firewall (WAF) yeterli mi?</h3>



<p class="wp-block-paragraph">WAF bazı şüpheli istekleri engelleyebilir. Ancak uygulamanın güvensiz sorgu oluşturma hatasını düzeltmez. WAF&#8217;ı ek koruma katmanı olarak değerlendirin.</p>



<h2 class="wp-block-heading">Sonuç: SQL Injection&#8217;a Karşı Güvenli Kodlama Alışkanlığı</h2>



<p class="wp-block-paragraph">SQL Injection, uygulamanın veri ile komut arasındaki sınırı kaybetmesiyle ortaya çıkar.</p>



<p class="wp-block-paragraph">Geliştirici kullanıcı girdisini SQL sorgusuna doğrudan eklediğinde saldırgan sorgunun davranışını etkileyebilir.</p>



<p class="wp-block-paragraph">Bu riski azaltmak için karmaşık güvenlik algoritmalarına ihtiyacımız yok. Öncelikle doğru sorgu oluşturma alışkanlığını benimsememiz gerekiyor.</p>



<p class="wp-block-paragraph">Java uygulamalarında <code>PreparedStatement</code> kullanmak, dinamik SQL parçalarını allowlist ile sınırlandırmak, veritabanı hesaplarının yetkilerini azaltmak ve sorguları düzenli test etmek güçlü bir başlangıç sağlar.</p>



<p class="wp-block-paragraph">En önemli kural oldukça basit:</p>



<p class="wp-block-paragraph"><strong>SQL komutlarını uygulama belirlesin. Kullanıcılar yalnızca veri sağlasın.</strong></p>



<p class="wp-block-paragraph">Bu ayrımı koruduğunuzda SQL Injection saldırılarının temel giriş noktasını kapatmış olursunuz.</p>



<h2 class="wp-block-heading">Kaynaklar</h2>



<ol start="1" class="wp-block-list">
<li>OWASP — SQL Injection Prevention Cheat Sheet<br><a href="https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html">https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html</a></li>



<li>OWASP — Top 10:2025, A05 Injection<br><a href="https://top10.owasp.org/2025/A05_2025-Injection/">https://top10.owasp.org/2025/A05_2025-Injection/</a></li>



<li>OWASP — Web Security Testing Guide<br><a href="https://owasp.org/www-project-web-security-testing-guide/">https://owasp.org/www-project-web-security-testing-guide/</a></li>



<li>Oracle — JDBC PreparedStatement<br><a href="https://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html">https://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html</a></li>
</ol>
<p><a href="https://alikarahisar.com/blog/sql-injection-nedir/">SQL Injection Nedir? Saldırıyı Anlamak ve Önlemek İçin Pratik Rehber</a> yazısı ilk önce <a href="https://alikarahisar.com/blog">Ali Karahisar</a> üzerinde ortaya çıktı.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://alikarahisar.com/blog/sql-injection-nedir/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
