{"componentChunkName":"component---src-templates-post-template-js","path":"/posts/11","result":{"data":{"markdownRemark":{"id":"1051769f-3ecc-5084-a85c-40313954f5b6","html":"<h3 id=\"첫-시도는-아래와-같이\"><a href=\"#%EC%B2%AB-%EC%8B%9C%EB%8F%84%EB%8A%94-%EC%95%84%EB%9E%98%EC%99%80-%EA%B0%99%EC%9D%B4\" aria-label=\"첫 시도는 아래와 같이 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>첫 시도는 아래와 같이</h3>\n<p><code class=\"language-text\">makeQuery()</code>로 만든 쿼리(string 타입)를 <code class=\"language-text\">cursor.execute(sql, [params])</code>에서 [params] 파라미터 중 하나로 보내면\n쿼리문으로 인식해 작동할 것이라 예상했으나, 오직 string으로만 인식하여 원하는 결과물을 얻지 못했다.</p>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token comment\"># BAD CASE</span>\n<span class=\"token keyword\">def</span> <span class=\"token function\">makeQuery</span><span class=\"token punctuation\">(</span>exam_content_id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">from</span> exam<span class=\"token punctuation\">.</span>models <span class=\"token keyword\">import</span> ExamField\n    sections <span class=\"token operator\">=</span> ExamField<span class=\"token punctuation\">.</span>objects<span class=\"token punctuation\">.</span><span class=\"token builtin\">filter</span><span class=\"token punctuation\">(</span>exam_content_id<span class=\"token operator\">=</span>exam_content_id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>order_by<span class=\"token punctuation\">(</span><span class=\"token string\">'id'</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">''</span><span class=\"token punctuation\">.</span>join<span class=\"token punctuation\">(</span>\n        <span class=\"token punctuation\">[</span><span class=\"token triple-quoted-string string\">\"\"\"\n            (select count(*) from exam_examuseranswer\n            inner join exam_examquestion on exam_examquestion.id = exam_examuseranswer.exam_question_id\n            inner join exam_question on exam_question.id = exam_examquestion.question_id\n            inner join exam_examfield on exam_examfield.id = exam_examquestion.exam_field_id\n            where exam_examuseranswer.user_exam_id = exam_userexamcontent.id and exam_examfield.id = %s and exam_question.answer = exam_examuseranswer.answer\n            ),\n        \"\"\"</span> <span class=\"token operator\">%</span> <span class=\"token punctuation\">(</span>section<span class=\"token punctuation\">.</span><span class=\"token builtin\">id</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">for</span> section <span class=\"token keyword\">in</span> sections<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">with</span> connection<span class=\"token punctuation\">.</span>cursor<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> cursor<span class=\"token punctuation\">:</span>\n    cursor<span class=\"token punctuation\">.</span>execute<span class=\"token punctuation\">(</span>\n        <span class=\"token triple-quoted-string string\">\"\"\"\n        select distinct exam_userexamcontent.created_at, exam_userexamcontent.id, temp_account.\"name\", temp_account.phone_number,\n            auth_user.username, auth_user.date_joined, %s\n            from exam_examuseranswer as eua\n            right join exam_userexamcontent on exam_userexamcontent.id = eua.user_exam_id\n            inner join exam_examtime on exam_examtime.id = exam_userexamcontent.exam_time_id\n            inner join payment_usercontent on payment_usercontent.id = exam_userexamcontent.user_content_id\n            inner join payment_order on payment_order.id = payment_usercontent.order_id\n            inner join temp_account on temp_account.id = payment_order.temp_account_id\n            left join user_userinfo on user_userinfo.id = temp_account.user_id\n            left join auth_user on auth_user.id = user_userinfo.user_id\n            inner join exam_examcontent on exam_examcontent.id = exam_examtime.exam_content_id\n            where exam_examcontent.id = %s order by exam_userexamcontent.id desc;\n        \"\"\"</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>makeQuery<span class=\"token punctuation\">(</span>exam_content_id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> exam_content_id<span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">)</span>\n    result <span class=\"token operator\">=</span> cursor<span class=\"token punctuation\">.</span>fetchall<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span></code></pre></div>\n<h3 id=\"중간-과정\"><a href=\"#%EC%A4%91%EA%B0%84-%EA%B3%BC%EC%A0%95\" aria-label=\"중간 과정 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>중간 과정</h3>\n<p><code class=\"language-text\">cursor.execute(sql, [params])</code>의 파라미터가 아닌 사전에 포맷팅하여 완성된 쿼리(string)를 <code class=\"language-text\">execute()</code>의 <code class=\"language-text\">sql</code>에 때려박으니, 원하는 결과값은 얻는다.</p>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\">query <span class=\"token operator\">=</span> <span class=\"token triple-quoted-string string\">\"\"\"\n        select distinct exam_userexamcontent.created_at, exam_userexamcontent.id, temp_account.\"name\", temp_account.phone_number,\n            auth_user.username, auth_user.date_joined, %s\n            from exam_examuseranswer as eua\n            right join exam_userexamcontent on exam_userexamcontent.id = eua.user_exam_id\n            inner join exam_examtime on exam_examtime.id = exam_userexamcontent.exam_time_id\n            inner join payment_usercontent on payment_usercontent.id = exam_userexamcontent.user_content_id\n            inner join payment_order on payment_order.id = payment_usercontent.order_id\n            inner join temp_account on temp_account.id = payment_order.temp_account_id\n            left join user_userinfo on user_userinfo.id = temp_account.user_id\n            left join auth_user on auth_user.id = user_userinfo.user_id\n            inner join exam_examcontent on exam_examcontent.id = exam_examtime.exam_content_id\n            where exam_examcontent.id = %s order by exam_userexamcontent.id desc;\n        \"\"\"</span> <span class=\"token operator\">%</span> <span class=\"token punctuation\">(</span>makeQuery<span class=\"token punctuation\">(</span>exam_content_id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> exam_content_id<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">from</span> django<span class=\"token punctuation\">.</span>db <span class=\"token keyword\">import</span> connection\n<span class=\"token keyword\">with</span> connection<span class=\"token punctuation\">.</span>cursor<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> cursor<span class=\"token punctuation\">:</span>\n    cursor<span class=\"token punctuation\">.</span>execute<span class=\"token punctuation\">(</span>query<span class=\"token punctuation\">)</span>\n    result <span class=\"token operator\">=</span> cursor<span class=\"token punctuation\">.</span>fetchall<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span></code></pre></div>\n<h3 id=\"근데-문제는-sql-injection이라는-것\"><a href=\"#%EA%B7%BC%EB%8D%B0-%EB%AC%B8%EC%A0%9C%EB%8A%94-sql-injection%EC%9D%B4%EB%9D%BC%EB%8A%94-%EA%B2%83\" aria-label=\"근데 문제는 sql injection이라는 것 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>근데 문제는 ‘SQL injection’이라는 것</h3>\n<p>장고 공식 문서를 보면 (<a href=\"https://docs.djangoproject.com/en/3.0/topics/db/sql/#passing-parameters-into-raw\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://docs.djangoproject.com/en/3.0/topics/db/sql/#passing-parameters-into-raw</a>)</p>\n<blockquote>\n<p>Warning :\nDo not use string formatting on raw queries or quote placeholders in your SQL strings!\nAs discussed in SQL injection protection, using the params argument and leaving the placeholders unquoted protects you from SQL injection attacks,\na common exploit where attackers inject arbitrary SQL into your database.\nIf you use string interpolation or quote the placeholder, you’re at risk for SQL injection.</p>\n</blockquote>\n<p>위와 같은 경고가 있다. ‘SQL injection attack’에 노출될 수 있으니, 포맷팅된 쿼리를 <code class=\"language-text\">execute()</code>에 때려박지 말라는 듯 하다.</p>\n<h3 id=\"그래서-아래와-같이-바꿔본다\"><a href=\"#%EA%B7%B8%EB%9E%98%EC%84%9C-%EC%95%84%EB%9E%98%EC%99%80-%EA%B0%99%EC%9D%B4-%EB%B0%94%EA%BF%94%EB%B3%B8%EB%8B%A4\" aria-label=\"그래서 아래와 같이 바꿔본다 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>그래서 아래와 같이 바꿔본다</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">def</span> <span class=\"token function\">makeQuery</span><span class=\"token punctuation\">(</span>sections<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    scores_query <span class=\"token operator\">=</span> <span class=\"token string\">''</span><span class=\"token punctuation\">.</span>join<span class=\"token punctuation\">(</span>\n        <span class=\"token punctuation\">[</span><span class=\"token triple-quoted-string string\">\"\"\"\n            (select count(*) from exam_examuseranswer\n            inner join exam_examquestion on exam_examquestion.id = exam_examuseranswer.exam_question_id\n            inner join exam_question on exam_question.id = exam_examquestion.question_id\n            inner join exam_examfield on exam_examfield.id = exam_examquestion.exam_field_id\n            where exam_examuseranswer.user_exam_id = exam_userexamcontent.id and exam_examfield.id = %s and exam_question.answer = exam_examuseranswer.answer\n            ),\n        \"\"\"</span> <span class=\"token keyword\">for</span> section <span class=\"token keyword\">in</span> sections<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token triple-quoted-string string\">\"\"\"\n        select distinct exam_userexamcontent.id, temp_account.\"name\", temp_account.phone_number,\n            auth_user.username, auth_user.date_joined, {scores_query} exam_userexamcontent.created_at\n            from exam_examuseranswer as eua\n            right join exam_userexamcontent on exam_userexamcontent.id = eua.user_exam_id\n            inner join exam_examtime on exam_examtime.id = exam_userexamcontent.exam_time_id\n            inner join payment_usercontent on payment_usercontent.id = exam_userexamcontent.user_content_id\n            inner join payment_order on payment_order.id = payment_usercontent.order_id\n            inner join temp_account on temp_account.id = payment_order.temp_account_id\n            left join user_userinfo on user_userinfo.id = temp_account.user_id\n            left join auth_user on auth_user.id = user_userinfo.user_id\n            inner join exam_examcontent on exam_examcontent.id = exam_examtime.exam_content_id\n            where exam_userexamcontent.created_at >= %s at time zone 'utc' at time zone 'kst'\n            and exam_userexamcontent.created_at &lt;= %s at time zone 'utc' at time zone 'kst'\n            and exam_examcontent.id = %s order by exam_userexamcontent.id desc;\n        \"\"\"</span><span class=\"token punctuation\">.</span><span class=\"token builtin\">format</span><span class=\"token punctuation\">(</span>scores_query<span class=\"token operator\">=</span>scores_query<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">makeParams</span><span class=\"token punctuation\">(</span>sections<span class=\"token punctuation\">,</span> start_date<span class=\"token punctuation\">,</span> end_date<span class=\"token punctuation\">,</span> exam_content_id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">[</span>section<span class=\"token punctuation\">.</span><span class=\"token builtin\">id</span> <span class=\"token keyword\">for</span> section <span class=\"token keyword\">in</span> sections<span class=\"token punctuation\">]</span> <span class=\"token operator\">+</span> <span class=\"token punctuation\">[</span>start_date<span class=\"token punctuation\">,</span> end_date<span class=\"token punctuation\">,</span> exam_content_id<span class=\"token punctuation\">]</span>\n\n<span class=\"token keyword\">with</span> connections<span class=\"token punctuation\">[</span>using_db_name<span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>cursor<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> cursor<span class=\"token punctuation\">:</span>\n    cursor<span class=\"token punctuation\">.</span>execute<span class=\"token punctuation\">(</span>makeQuery<span class=\"token punctuation\">(</span>sections<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> makeParams<span class=\"token punctuation\">(</span>sections<span class=\"token punctuation\">,</span> start_date<span class=\"token punctuation\">,</span> end_date<span class=\"token punctuation\">,</span> exam_content_id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    results <span class=\"token operator\">=</span> cursor<span class=\"token punctuation\">.</span>fetchall<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>makeQuery 함수에선 query를, makeParams 함수에선 params만을 반환하게 역할을 명확히 분리시켰다.</p>\n<ul>\n<li><code class=\"language-text\">makeQuery()</code>에서 <code class=\"language-text\">[params]</code>(파라미터)로 채워질 <code class=\"language-text\">%s</code>까지 포함한 문자열 타입의 쿼리를 리턴한다.</li>\n<li><code class=\"language-text\">makeParams()</code>에서 파라미터 값들을 가진 배열을 리턴한다.</li>\n</ul>\n<p>이러면 ‘SQL injection’ 위험도 피하고, 원하는 대로 쿼리와 파라미터를 만들어\n원하는 결과를 얻을 수 있었다.</p>","fields":{"slug":"/posts/11","tagSlugs":null},"frontmatter":{"date":"2020-03-19","description":"","tags":null,"title":"Django Raw Query, cursor.execute()"}}},"pageContext":{"isCreatedByStatefulCreatePages":false,"slug":"/posts/11"}}}