Για έναν έμπειρο προγραμματιστή, αυτό το κείμενο μπορεί να μην προκαλεί καμία έκπληξη, αλλά νομίζω ότι πολλά άρθρα που έχω διαβάσει σχετικά με τη ρύθμιση CORS στο Rails έλεγαν κάτι σαν: χρησιμοποιήστε το rack-cors, επιτρέψτε σε οποιονδήποτε υπολογιστή να έχει πρόσβαση στο API και (προαιρετικά): θα πρέπει να εξετάσετε κάτι διαφορετικό (από το να επιτρέπετε σε οποιονδήποτε υπολογιστή) στην παραγωγή.
Η προτεινόμενη κωδικός ήταν πάντα κοντά σε αυτό που βρισκόταν από κάτω:
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins ''
resource '', headers: :any, methods: :any
end
end
και, δυστυχώς, αυτά τα κείμενα δεν μας εξηγούσαν σχεδόν καθόλου τι πρέπει να κάνουμε στην παραγωγή.
Είμαι αρκετά εντάξει με την αντιγραφή-επικόλληση (Μερικές φορές αστειεύομαι ότι οι εταιρείες θα μπορούσαν να προσλάβουν έναν αντιγραφέα του Stack Overflow), εφόσον υπάρχει μια στιγμή "σκέψης και προσαρμογής" μεταξύ "αντιγραφής" και "επικόλλησης". Θα ήθελα λοιπόν να αναλύσω λίγο τι κάνουμε εδώ και πώς λειτουργεί στην πραγματική ζωή.
Ελπίζω να μην σας πειράζει που θα ξεκινήσω με μια σύντομη εισαγωγή στη θεωρία της τιμής και στη συνέχεια θα περάσω στα παραδείγματα του Rails.
Εισαγωγή
Ας ξεκινήσουμε από την αρχή. Για να εξηγήσω καλύτερα τα πράγματα, έχω χωρίσει την εισαγωγή σε τρία μέρη. Στο πρώτο μέρος θα περιγράψω τι είναι η προέλευση - ο όρος-κλειδί για αυτό που συζητάμε εδώ. Το δεύτερο αφορά το SOP, απλώς μια σύντομη περιγραφή. Και το τελευταίο μέρος μιλάει για την CORS
η ίδια.
Τι είναι η προέλευση;
Σύμφωνα με τα έγγραφα του MDN Web Docs:
- Η προέλευση του διαδικτυακού περιεχομένου καθορίζεται από το σχήμα (πρωτόκολλο), τον κεντρικό υπολογιστή (τομέα) και τη θύρα της διεύθυνσης URL που χρησιμοποιείται για την πρόσβαση σε αυτό. Δύο αντικείμενα έχουν την ίδια προέλευση μόνο όταν το σχήμα, ο κεντρικός υπολογιστής και η θύρα ταιριάζουν (πηγή)
Αυτό φαίνεται αρκετά ξεκάθαρο, έτσι δεν είναι; Ας αναλύσουμε δύο παραδείγματα από το MDN, για κάθε περίπτωση.
http://example.com/app1/index.html
, http://example.com/app2/index.html
Τα 2 παραπάνω έχουν την ίδια προέλευση, διότι:
- τα συστήματά τους (http) είναι τα ίδια,
- τα domains τους (example.com) είναι τα ίδια,
- οι θύρες τους (implicit) είναι οι ίδιες.
http://www.example.com
, http://myapp.example.com
Αυτά τα 2 έχουν διαφορετική προέλευση επειδή οι τομείς (www.example.com
, myapp.example.com
) είναι διαφορετικές.
Ελπίζω να είναι αρκετά σαφές. Αν όχι, παρακαλούμε επισκεφθείτε τα MDN Web Docs για περισσότερα παραδείγματα.
Τι είναι το SOP;
Τα έγγραφα του MDN Web Docs λένε (πηγή):
- Η πολιτική ίδιας προέλευσης είναι ένας κρίσιμος μηχανισμός ασφαλείας που περιορίζει τον τρόπο με τον οποίο ένα έγγραφο ή μια δέσμη ενεργειών που φορτώνεται από μια προέλευση μπορεί να αλληλεπιδράσει με έναν πόρο από μια άλλη προέλευση. Βοηθά στην απομόνωση δυνητικά κακόβουλων εγγράφων, μειώνοντας τους πιθανούς φορείς επιθέσεων.
- Συνήθως επιτρέπονται οι εγγραφές διασταυρούμενης προέλευσης. Παραδείγματα είναι οι σύνδεσμοι, οι ανακατευθύνσεις και οι υποβολές φόρμας.
- Συνήθως επιτρέπεται η διασταυρούμενη ενσωμάτωση.
- Οι διασταυρούμενες αναγνώσεις συνήθως δεν επιτρέπονται, αλλά η πρόσβαση στην ανάγνωση συχνά διαρρέει με την ενσωμάτωση.
Χρήση CORS
για να επιτρέπεται η διασταυρούμενη πρόσβαση
Λοιπόν, όπως μπορείτε να δείτε, υπάρχουν πολλά σχετικά με τη διασταυρούμενη συμπεριφορά στους ορισμούς της SOP. Αυτό είναι εντάξει. Το μόνο που πρέπει να γνωρίζουμε τώρα είναι ότι η ίδια προέλευση έχει περισσότερα προνόμια και μπορούμε να χαλαρώσουμε τους κανόνες για τις διασταυρούμενες προελεύσεις χρησιμοποιώντας το CORS. Και εδώ έρχεται η επόμενη ενότητα.
Τι είναι το CORS;
Με βάση τα λόγια του MDN:
- Ο διαμοιρασμός πόρων πολλαπλής προέλευσης (CORS) είναι ένας μηχανισμός που βασίζεται στην κεφαλίδα HTTP και επιτρέπει σε έναν διακομιστή να υποδεικνύει οποιαδήποτε άλλη προέλευση (τομέας, σύστημα ή θύρα) από τη δική του, από την οποία ένα πρόγραμμα περιήγησης θα πρέπει να επιτρέπει τη φόρτωση πόρων. Το CORS βασίζεται επίσης σε έναν μηχανισμό με τον οποίο οι φυλλομετρητές υποβάλλουν ένα αίτημα "προ-εκτόξευσης" στον διακομιστή που φιλοξενεί τον πόρο διασταυρούμενης προέλευσης, προκειμένου να ελέγξουν ότι ο διακομιστής θα επιτρέψει την πραγματική αίτηση. Σε αυτό το preflight, το πρόγραμμα περιήγησης στέλνει κεφαλίδες που υποδεικνύουν τη μέθοδο HTTP και τις κεφαλίδες που θα χρησιμοποιηθούν στην πραγματική αίτηση. (πηγή).
Αυτό δεν είναι ακόμα αρκετό. Αυτό που δεν ειπώθηκε εκεί ρητά είναι ότι η πιο σημαντική επικεφαλίδα όταν χρησιμοποιείτε CORS
είναι Access-Control-Allow-Origin
:
- Το
Access-Control-Allow-Origin
κεφαλίδα απάντησης υποδεικνύει εάν η απάντηση μπορεί να μοιραστεί με τον αιτούντα κώδικα από τη συγκεκριμένη προέλευση (πηγή).
Λοιπόν, αυτό πρέπει να είναι. Στην πραγματική ζωή, κατά τη διαμόρφωση CORS
, συνήθως ρυθμίζουμε το ACAO
επικεφαλίδα πρώτα.
Πραγματική ζωή
Αυτό είναι όλο, όταν πρόκειται για ορισμούς. Ας επιστρέψουμε στο Rails και στα παραδείγματα της πραγματικής ζωής.
Πώς να ρυθμίσετε το CORS στο Rails;
Σίγουρα θα χρησιμοποιήσουμε rack-cors (όπως μας είπαν). Ας θυμηθούμε το πρώτο απόσπασμα, αυτό που παρέχεται πιο συχνά σε άλλα άρθρα:
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins ''
resource '', headers: :any, methods: :any
end
end
Ο αριθμός των επιλογών είναι τεράστιος ή και άπειρος, αλλά ας εξετάσουμε αυτές τις δύο:
- δημιουργούμε το API που επιτρέπεται να χρησιμοποιείται από τρίτους πελάτες προγραμμάτων περιήγησης,
- έχουμε τυπικό διαχωρισμό frontend/backend και θέλουμε να επιτρέψουμε στους έμπιστους πελάτες μας να έχουν πρόσβαση στο API.
Κτίριο API με πρόσβαση από τρίτους πελάτες
Εάν αντιμετωπίζετε την πρώτη επιλογή, πιθανώς θα μπορούσατε να επιλέξετε προέλευση
'*' - θέλετε άλλοι να δημιουργήσουν έναν πελάτη πάνω στο API σας και δεν ξέρετε ποιοι είναι, σωστά;
Τυπικός διαχωρισμός frontend/backend
Εάν αναπτύσσετε το τελευταίο, πιθανώς δεν θέλετε να κάνουν όλοι αιτήσεις διασταυρούμενης προέλευσης στο API σας. Μάλλον θέλετε να:
- επιτρέψτε στους πελάτες παραγωγής να έχουν πρόσβαση στο API παραγωγής,
- το ίδιο και για τη σκηνοθεσία,
- το ίδιο για το localhost,
- μπορεί να θέλετε να επιτρέψετε στις εφαρμογές αναθεώρησης FE να έχουν πρόσβαση στο staging.
Θα εξακολουθήσουμε να χρησιμοποιούμε ρακόρ (όπως μας είπαν) - αλλά με τον δικό μας τρόπο.
Ας χρησιμοποιήσουμε 2 μεταβλητές ENV: ALLOWED_ORIGINS
για κυριολεκτικούς ορισμούς προέλευσης (αστερίσκος ή πραγματική διεύθυνση URL) και ALLOWED_ORIGIN_REGEXPS
για τα μοτίβα.
config/initializers/cors.rb
frozenstringliteral: true
toregexp = ->(string) { Regexp.new(string) }
hosts = [
*ENV.fetch('ALLOWEDORIGINS').split(','),
*ENV.fetch('ALLOWEDORIGINREGEXPS').split(';').map(&to_regexp)
]
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins(*hosts)
resource '*',
methods: %i[get post put patch delete options head],
headers: :any
end
end
Τι συμβαίνει εδώ;
- Όπως μπορείτε να δείτε, χωρίζουμε τις τιμές που ορίζονται στις μεταβλητές ENV με διαφορετικά διαχωριστικά. Αυτό συμβαίνει επειδή μια άνω τελεία είναι λιγότερο πιθανό να εμφανιστεί στο μοτίβο ορισμού URL.
- Οι κυριολεκτικές τιμές είναι έτοιμες για χρήση, αλλά πρέπει να αντιστοιχίσουμε τα μοτίβα σε πραγματικές περιπτώσεις Regexp.
- Στη συνέχεια, τα ενώνουμε όλα μαζί και επιτρέπουμε σε αυτούς τους κεντρικούς υπολογιστές να έχουν πρόσβαση σε οποιονδήποτε πόρο με μεθόδους που περιλαμβάνονται στη λευκή λίστα που χρησιμοποιεί το API μας.
Αυτό θα σας δώσει αρκετή ευελιξία για να ορίσετε τις κατάλληλες τιμές στα περιβάλλοντα ανάπτυξης, προσωρινής αποθήκευσης και παραγωγής.
Συμπεράσματα
Ας συνοψίσουμε όλα τα παραπάνω σε βασικά σημεία:
- χρησιμοποιείτε μεταβλητές ENV για να ρυθμίσετε
CORS
,
- να χρησιμοποιείτε κανονικές εκφράσεις για να επιτρέπετε σε διαφορετικές προελεύσεις να έχουν πρόσβαση στο API σταδιοποίησης (π.χ. για εφαρμογές επισκόπησης),
- βάζετε πάντα το "σκέφτομαι και προσαρμόζω" μεταξύ του "αντιγράφω" και του "επικολλώ".
Αυτό είναι όλο. Καλή σας μέρα! 🙂
Διαβάστε περισσότερα:
Γιατί πρέπει (πιθανώς) να χρησιμοποιήσετε την Typescript;
10 νεοφυείς επιχειρήσεις της Νέας Υόρκης που αξίζει να αναφερθούν το 2021