Theory
સપાટ files મોટા પાયે ચાલતી નથી
Portal અત્યાર સુધી registrations ને લખાણની file માં સંઘરતું હતું. નમૂના પૂરતું એ ચાલે, પણ ખરા મહોત્સવ માટે એ તૂટી પડે: સહેલી શોધ નહીં, ક્રમ ગોઠવવાની સગવડ નહીં, અને બે જણ એકસાથે registration કરે તો દોડાદોડી. ખરો data DATABASE માં રહે છે, અને SQL તો તમે BCA205 તથા BCA303 માંથી પહેલેથી જાણો છો.
આ પાઠ PHP ને MySQL સાથે જોડે છે અને તમારાં pages માંથી પૂરેપૂરું CRUD ચલાવે છે. પણ એની શરૂઆત ચેતવણીથી થાય છે, કારણ કે PHP ની applications સૌથી વધુ અહીં જ hack થાય છે: વપરાશકારે ભરેલું લખાણ SQL ની query ને અડે એ ક્ષણે તમારે SQL injection સામે બચાવ કરવો જ પડે, અને એ બચાવ એટલે prepared statements.
Theory
જોડાણ, અને CRUD
PHP MySQL સાથે 2 સામાન્ય આંતરમુખ દ્વારા વાત કરે છે:
- mysqli: ફક્ત MySQL માટે
- PDO: ઘણાં databases સાથે ચાલે છે (એટલે ખસેડી શકાય એવું), એથી ઘણા એને પસંદ કરે છે
જોડાણ થયા પછી તમે BCA205 વાળું જ SQL ચલાવો છો:
- બનાવવા INSERT, વાંચવા SELECT, બદલવા UPDATE, કાઢવા DELETE
- ઉપવાક્યો: WHERE (rows ગાળવી), ORDER BY (ક્રમ ગોઠવવો), LIMIT (સંખ્યા મર્યાદિત કરવી)
એટલે SELECT name FROM regs WHERE event = 'garba' ORDER BY name LIMIT 10 એ Garba Night માટે નોંધાયેલા પહેલા 10 જણને મૂળાક્ષરના ક્રમમાં લાવે છે. ભારે કામ database કરે છે; PHP તો ફક્ત query મોકલે છે અને પરિણામ વાંચે છે. (Tables ને બદલે document વાળા data માટે MongoDB એ NoSQL વિકલ્પ છે, જે BCA503-01 માં આવે છે.)
Watch out
SQL injection: રોકવો જ પડે એવો હુમલો
ધારો કે તમે વપરાશકારે ભરેલા લખાણને જોડીને query બનાવો છો:
"SELECT * FROM users WHERE name = '" . $_POST['name'] . "'"
વપરાશકાર પોતાના નામ તરીકે ' OR '1'='1 લખે છે. Query બની જાય છે ... WHERE name = '' OR '1'='1', જે હંમેશા સાચી હોય છે, એટલે દરેક વપરાશકારની વિગત બહાર નીકળી જાય, કે એથી પણ ખરાબ થાય. આ જ છે SQL injection: વપરાશકારે ભરેલું લખાણ SQL તરીકે ઘુસાડી દેવું. આ વેબની જાણીતી અને વિનાશક નબળાઈ છે, અને લખાણને SQL માં જોડવું એ જ એને આમંત્રણ આપવાની રીત છે.
Practical
ઉપાય: prepared statement (bound parameter)
<?php
$db = new mysqli("localhost", "root", "", "portal");
// PREPARED STATEMENT: the query structure is fixed first,
// then the value is bound separately and can NEVER become SQL.
$stmt = $db->prepare("SELECT name FROM regs WHERE event = ?");
$stmt->bind_param("s", $_POST["event"]); // 's' = string param
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
echo htmlspecialchars($row["name"]) . "<br>";
}
$stmt->close();
// The ? is a PLACEHOLDER. Even if event is "' OR '1'='1",
// it is treated as a literal value, not as SQL. Injection blocked.
?>
Theory
Prepared statements શા માટે સલામત છે
Prepared statement પહેલાં query નું માળખું database ને મોકલે છે, જ્યાં કિંમતો આવવાની હોય ત્યાં ? વાળા placeholders સાથે. Database એ માળખું compile કરે છે, પછી તમે ખરી કિંમતો અલગથી bind કરો છો. કિંમતો માળખું નક્કી થયા પછી આવે છે, એટલે એ ફક્ત DATA જ બની શકે, SQL કદી નહીં: ' OR '1'='1 વાળી bind થયેલી કિંમત query બદલવાને બદલે અક્ષરશઃ એ જ string તરીકે શોધાય છે અને કશા સાથે મેળ ખાતી નથી.
આખો બચાવ એટલો જ છે: query ના ઘાટને એની કિંમતોથી અલગ કરો. નિયમ સંપૂર્ણ છે: વપરાશકારે ભરેલું લખાણ SQL માં કદી જોડશો નહીં; હંમેશા bound parameters વાળાં prepared statements વાપરો. પરીક્ષકો ખાસ આ જ જોવા માગે છે.
Quiz
Query માં વપરાશકારે ભરેલું લખાણ વપરાતું હોય ત્યારે SQL injection સામેનો સાચો બચાવ કયો છે?
- લખાણને જોડો, પણ પહેલાં એને મોટા અક્ષરોમાં ફેરવો
- Bound parameters વાળાં prepared statements વાપરો, જેથી વપરાશકારનું લખાણ data ગણાય અને SQL ને કદી બદલી ન શકે
- Form માં client બાજુની ચકાસણી હોય તો લખાણ પર ભરોસો કરો
- ફક્ત SELECT વાળી queries વાપરો, INSERT કદી નહીં
Show the answer
Bound parameters વાળાં prepared statements વાપરો, જેથી વપરાશકારનું લખાણ data ગણાય અને SQL ને કદી બદલી ન શકે
Bound parameters વાળાં prepared statements એ જ ખરો બચાવ છે: query નું માળખું placeholders સાથે નક્કી થઈ જાય છે, કિંમતો અલગથી bind થાય છે, એટલે ' OR '1'='1 જેવું દુષ્ટ લખાણ પણ અક્ષરશઃ કિંમત ગણાય છે અને SQL બદલી શકતું નથી. વિકલ્પ A કશું કરતો નથી (મોટા અક્ષરે ફેરવવાથી injection અટકતું નથી). વિકલ્પ C એ જ ઘાતક ભ્રમ દોહરાવે છે કે client બાજુની તપાસ સલામતી છે: એને ટાળી શકાય છે, અને injection તો server ને જ નિશાન બનાવે છે. વિકલ્પ D સમસ્યા સમજ્યો જ નથી: injection કોઈ પણ પ્રકારની query ને અસર કરી શકે; ફક્ત SELECT પૂરતું મર્યાદિત રહેવાથી એ અટકતું પણ નથી અને એ વ્યવહારુ પણ નથી. હંમેશા query ના ઘાટને એની કિંમતોથી અલગ રાખો.
Think first
હુમલો વાંચો, પછી ઉપાય
એક નબળી query $_POST['name'] ને જોડે છે અને હુમલાખોર ' OR '1'='1 લખે છે. નબળી આવૃત્તિમાં અને prepared આવૃત્તિમાં database ને શું મળે છે તે તપાસો. પછી tap કરો.
Show the answer
નબળી આવૃત્તિ (જોડાણવાળી): database ને એક જ ભળી ગયેલો string મળે છે: WHERE name = '' OR '1'='1', જેમાં હુમલાખોરનું અવતરણચિહ્ન ધારેલા string ને બંધ કરી દે છે અને OR '1'='1' એ SQL ના તર્કનો ભાગ બની જાય છે, જે હંમેશા સાચો હોય, એટલે દરેક row પાછી આવે છે. લખાણ code બની ગયું. Prepared આવૃત્તિ: database ને પહેલાં WHERE name = ? મળે છે (માળખું નક્કી), પછી અલગથી ' OR '1'='1 એ કિંમત ? સાથે bind થયેલા DATA તરીકે મળે છે; એ અક્ષરશઃ એ વિચિત્ર string જેટલું name શોધે છે, એકેય મળતું નથી, અને કશું પાછું આવતું નથી. લખાણ data જ રહ્યું. સલામતીનો આખો ફરક એટલો જ છે: જોડાણ લખાણને SQL બનવા દે છે; binding લખાણને કિંમત તરીકે જ રાખે છે. કદી જોડશો નહીં.
Watch out
Database ના ફાંદા
લખાણને SQL માં જોડવું: injection નું બાકોરું; હંમેશા prepared statements વાપરો.
Database ની કિંમતો કાચી છાપવી: બતાવતી વખતે તો પણ એમના પર htmlspecialchars() કરો (XSS એ injection થી જુદું છે).
જોડાણની ભૂલો અવગણવી: query કરતાં પહેલાં જોડાણ સફળ થયું છે કે નહીં તે તપાસો.
Passwords સાદા લખાણમાં સંઘરવા: એમને hash કરો (password_hash); કાચા passwords કદી ન સંઘરો.
મોટાં tables પર LIMIT ભૂલી જવું: મર્યાદા વગરની SELECT ભારે મોટાં પરિણામ પાછાં લાવી શકે છે.
Theory
Data સંઘરાયો; હવે એને જીવંત કરો
Portal ના registrations હવે પૂરેપૂરા CRUD અને injection સામે સલામત queries સાથે MySQL માં સલામત રીતે રહે છે. હવે એ page ફરી load કર્યા વગર જીવંત બને છે: AJAX પડદા પાછળ તમારા PHP ને requests મોકલે છે (BCA405-01 વાળી રીત), જેથી નોંધાયેલા લોકોની જીવંત શોધ જેવી સગવડો બને. પછી Unit 3 ના છેલ્લા પાઠ આખા portal ને CodeIgniter ના MVC framework પર ફરી બાંધે છે: એટલે કે files, database અને routing ને એકસાથે બાંધતું વ્યાવસાયિક માળખું.
Summary
Key takeaways
- PHP MySQL સાથે mysqli (ફક્ત MySQL માટે) કે PDO (ઘણાં databases સાથે ચાલે એવું) દ્વારા જોડાય છે.
- PHP માંથી CRUD SQL વાપરે છે: INSERT, SELECT, UPDATE, DELETE, સાથે WHERE (ગાળવું), ORDER BY (ક્રમ), LIMIT (મર્યાદા).
- SQL injection: query માં જોડાયેલું વપરાશકારનું લખાણ SQL બદલી શકે છે (' OR '1'='1 શરતોને હંમેશા સાચી બનાવી દે છે).
- બચાવ: bound parameters વાળાં prepared statements: પહેલાં query નું માળખું નક્કી થાય, પછી કિંમતો data તરીકે અલગથી bind થાય.
- વપરાશકારે ભરેલું લખાણ SQL માં કદી જોડશો નહીં; bind થયેલી કિંમતો કદી SQL બની શકતી નથી.
- વળી passwords ને hash કરો (password_hash) અને database ની કિંમતો બતાવતી વખતે htmlspecialchars કરો.
- યાદ રાખવાની કડી: query ના ઘાટને એની કિંમતોથી અલગ કરો; bind કરો, જોડો નહીં.