Python には多くの利点がありますが、その 1 つは「すぐに使える」原則です。Python をインストールすると、多数の標準ソフトウェア パッケージが同時にインストールされるため、自分でダウンロードしなくてもすぐに使用できます。 Django もこの原則に従っており、独自の標準ライブラリも含まれています。この章では、これらの統合されたサブフレームワークについて説明します。
Django 標準ライブラリ
Django の標準ライブラリは django.contrib パッケージに配置されています。各サブパッケージは、追加機能の独立したパッケージです。通常、これらは必ずしも相互に関連しているわけではありませんが、一部の django.contrib サブパッケージは他のパッケージに依存する場合があります。
django.contrib の関数の種類に必須の要件はありません。これらのパッケージにはモデルが付属しているものもありますが (そのため、対応するテーブルをデータベースにインストールする必要があります)、その他のパッケージはスタンドアロンのミドルウェアとテンプレート タグで構成されています。
django.contrib 開発パッケージの共通の特徴は、django.contrib 開発パッケージ全体を削除しても、問題が発生することなく
Django の基本機能を引き続き使用できることです。 Django 開発者がフレームワークに新しい機能を追加する場合、新しい機能を django.contrib に追加するかどうかを決定する際には、この原則に厳密に従います。
django.contrib は次の開発パッケージで構成されています:
§ admin: 自動サイト管理ツール。 Django 管理サイトをチェックしてください
§ auth: Django のユーザー認証フレームワーク。セッション、ユーザー、登録を確認してください
§ コメント: 現在、このアプリケーションは鋭意開発中であるため、このアプリケーションの詳細については、この本の出版時点では提供できません。詳細については、Django の公式 Web サイトを参照してください。
§ contenttypes: これはドキュメント タイプ フックのフレームワークであり、インストールされた各 Django モジュールは独立したドキュメント タイプとして機能します。このフレームワークは主に Django 内で他のアプリケーションによって使用され、主に上級 Django 開発者を対象としています。このフレームワークの詳細については、django/contrib/contenttypes/.
§ csrf にあるソース コードを参照してください。このモジュールは、クロスサイト リクエスト フォージェリ (CSRF) を防御するために使用されます。タイトル「CSRF 防御」サブセクションに続きます。
§ フラットページ: データベース内の単一の HTML コンテンツを管理するモジュール。以下の「フラットページ」というタイトルのセクションを参照してください。
§ humanize: データの人間化を強化するために使用される一連の Django
モジュール フィルター。
§ マークアップ: いくつかの一般的なマークアップ言語を実装するために使用される一連の Django
テンプレート フィルター。
§ リダイレクト: リダイレクトを管理するために使用されるフレームワーク。
§ セッション: Django のセッション フレームワーク。セッション、ユーザー、登録を参照してください。
§ サイトマップ: サイトマップ用の XML
ファイルを生成するためのフレームワーク。 「Django による非 HTML コンテンツの出力」を参照してください。
§ サイト: 同じデータベースと Django インストール内で複数の Web サイトを管理できるようにするフレームワーク。
§ シンジケーション: RSS
と Atom を使用してシンジケーション フィードを生成するためのフレームワーク。 「Django による非 HTML コンテンツの出力」を参照してください。
この章の次の部分では、これまで紹介されていない django.contrib 開発パッケージの内容について詳しく説明します。
複数のサイト
Django のマルチサイト システムは、同じデータベースおよび同じ Django プロジェクトの下で複数の Web サイトを運用できるようにするユニバーサル フレームワークです。これは抽象的な概念であり、理解するのが少し難しい場合があります。そのため、これが役立ついくつかの実際的なシナリオから始めましょう。
シナリオ 1: 複数のサイトのデータを再利用する
最初の章で述べたように、Django によって構築された Web サイト LJWorld.com と Lawrance.com
は、Controlled by によって使用されます。同じ報道機関: カンザス州ローレンスのローレンス・デイリー・ワールド新聞。 LJWorld.com は主にニュースを提供し、
Lawrence.com は地元のエンターテイメントに重点を置いています。ただし、場合によっては、編集者が 2 つのサイトに記事を公開する必要がある場合があります。
この問題に対する行き止まりの解決策は、サイトごとに異なるデータベースを使用し、同じ記事を 2 回公開するようサイト管理者に依頼することです。1 回目は LJWorld.com、もう 1 回目は Lawrence.com です。しかし、これはサイト管理者にとって非効率であり、同じ記事の複数のコピーをデータベースに保持するのは冗長です。
より良い解決策はありますか? 2 つの Web サイトは同じ記事データベースを使用し、多対多の関係を使用して各記事を 1 つ以上のサイトに関連付けます。 Django サイト フレームワークは、どの記事を関連付けることができるかを記録するデータベースを提供します。これは、データを 1 つ以上のサイトに関連付けるフックです。
シナリオ 2: Web サイト名/ドメインを独自の場所に保存します
LJWorld.com と Lawrence.com の両方には、ニュースが発生するとすぐに通知を受け取るように読者がサインアップできる電子メール アラートがあります。これは完璧なメカニズムです。読者が登録フォームを送信すると、すぐに「ご登録いただきありがとうございます」というメールが届きます。
この登録プロセスのコードを 2 回実装するのは明らかに非効率で冗長であるため、2 つのサイトはバックグラウンドで同じコードを使用します。ただし、サインアップに対する感謝の通知は両方の Web サイトで異なる必要があります。 Site オブジェクトを使用すると、現在のサイトの名前 (たとえば、「LJWorld.com」) とドメイン (たとえば、「www.ljworld.com」) を使用して感謝通知を抽出できます。
Django のマルチサイト フレームワークは、Django プロジェクト内の各サイトの名前とドメインを保存する場所を提供します。つまり、これらの値を同じ方法で再利用できます。
マルチサイト フレームワークの使用方法
マルチサイト フレームワークは、フレームワークというよりも、一連の規約です。すべては 2 つの単純な概念に基づいています:
§ django.contrib.sites にあるサイト モデルには、ドメインと名前という 2 つのフィールドがあります。
§ SITE_ID 設定では、特定の構成ファイルに関連付けられたサイト オブジェクトのデータベース ID を指定します。
これら 2 つの概念をどのように使用するかはあなた次第ですが、Django はいくつかの簡単な規則に従って自動的にそれを行います。
マルチサイト アプリケーションをインストールするには、次の手順を実行する必要があります:
1. INSTALLED_APPS に「django.contrib.sites」を追加します。
2. manage.py syncdb コマンドを実行して、django_site テーブルをデータベースにインストールします。
3. Django 管理バックエンドまたは Python API を通じて 1 つ以上の
「Site」オブジェクトを追加します。この Django
プロジェクトでサポートされているサイト (またはドメイン) ごとに Site オブジェクトを作成します。
4. 各設定ファイルで SITE_ID 変数を定義します。この変数の値は、構成ファイルでサポートされているサイトの Site オブジェクトのデータベース
ID である必要があります。
マルチサイト フレームワークの機能
以下のセクションでは、マルチサイト フレームワークを使用して実行できるいくつかのタスクについて説明します。
複数のサイト間でのデータの再利用
シナリオ 1 で説明したように、複数のサイト間でデータを再利用するには、モデルにサイトの多対多フィールドを追加するだけです。例:
from django.db import models from django.contrib.sites.models import Site class Article(models.Model): headline = models.CharField(maxlength=200) # ... sites = models.ManyToManyField(Site)
Thisこれは、複数のサイトのデータベース内の記事をリンクするための基本的な手順です。この手法を適切に使用すると、Django ビュー コードの同じ部分を複数のサイトで再利用できます。 Article モデルの例を続けると、ここでは、article_detail ビューが考えられます:
from django.conf import settings def article_detail(request, article_id): try: a = Article.objects.get(id=article_id, sites__id=settings.SITE_ID) except Article.DoesNotExist: raise Http404
# ...
このビュー メソッドは、SITE_ID で設定された値に基づいて記事サイトを動的にチェックするため、再利用可能です。
たとえば、LJWorld.coms 設定ファイルの SITE_ID は 1 に設定され、
Lawrence.coms 設定ファイルの SITE_ID は 2 に設定されます。 LJWorld.coms がアクティブなときにこのビューが呼び出された場合、サイトのリストに
LJWorld.com が含まれる記事に検索が制限されます。
コンテンツを単一のサイトに関連付ける
同様に、外部キーを使用して、モデルを多対 1 の関係でサイト モデルに関連付けることもできます。
たとえば、特定の記事が 1 つのサイトにのみ表示される場合は、次のようなモデルを使用できます:
from django.db import models from django.contrib.sites.models import Site class Article(models.Model): headline = models.CharField(maxlength=200) # ... site = models.ForeignKey(Site)
これは、前のセクションで紹介したものと同じくらい有益です。
ビューから現在のサイトをフックします
内部では、Django ビューのマルチサイト フレームワークを使用することで、呼び出し元のサイトに応じてビューにさまざまな処理を実行させることができます。例:
from django.conf import settings def my_view(request): if settings.SITE_ID == 3: # Do something. else: # Do something else.
もちろん、そのようにサイト ID をハードコーディングするのは醜いです。これを達成する少し簡単な方法は、現在のサイト ドメインを表示することです:
from django.conf import settings from django.contrib.sites.models import Site def my_view(request): current_site = Site.objects.get(id=settings.SITE_ID) if current_site.domain == 'foo.com': # Do something else: # Do something else.
Site オブジェクトから settings.SITE_ID 値を取得するのが一般的であるため、サイト モデル マネージャー
(Site.objects) には get_current() メソッドがあります。 。次の例は、前の例と同等です。
from django.contrib.sites.models import Site def my_view(request): current_site = Site.objects.get_current() if current_site.domain == 'foo.com': # Do something else: # Do something else.
注
この最後の例では、django.conf.settings をインポートしません。
レンダリング用に現在のドメインを取得します
シナリオ 2 で説明したように、サイト名とドメイン名を保存する DRY (Dontrepeat Yourself) 方法の場合 (サイト名とドメイン名を 1 つの場所に保存します) )、現在のサイト オブジェクトの名前とドメインを参照するだけです。例:
from django.contrib.sites.models import Site from django.core.mail import send_mail def register_for_newsletter(request): # Check form values, etc., and subscribe the user. # ... current_site = Site.objects.get_current() send_mail('Thanks for subscribing to %s alerts' % current_site.name, 'Thanks for your subscription. We appreciate it./n/n-The %s team.' % current_site.name, 'editor@%s' % current_site.domain, [user_email]) # ...
LJWorld.com と Lawrence.com の続きです
私たちが議論していた例では、Lawrence.com では
メールの件名は「Lawrence.com リマインダー メールにご登録いただきありがとうございます。」です。
LJWorld.com では、メールの件名は「LJWorld.com リマインダーメールにご登録いただきありがとうございます」です。このサイト関連の動作は、電子メール メッセージの件名にも適用されます。
これを実現するためのより柔軟な (ただし負荷が高い) 方法は、Django のテンプレート システムを使用することです。 Lawrence.com と
LJWorld.com にそれぞれ異なるテンプレート ディレクトリ (TEMPLATE_DIRS) があると仮定すると、次のように作業をテンプレート システムに簡単に転送できます。
from django.core.mail import send_mail from django.template import loader, Context def register_for_newsletter(request): # Check form values, etc., and subscribe the user. # ... subject = loader.get_template('alerts/subject.txt').render(Context({})) message = loader.get_template('alerts/message.txt').render(Context({})) send_mail(subject, message, 'do-not-reply@example.com', [user_email]) # ...
この場合、LJWorld.com を作成する必要があります。 Lawrence.com のテンプレート ディレクトリにある subject.txt および message.txt テンプレート
。前述したように、このアプローチでは柔軟性が高まりますが、複雑さも増します。
できるだけ多くの Site オブジェクトを使用することは、不必要な複雑で冗長な作業を減らす良い方法です。
現在のドメインの完全な URL を取得します
Django 的get_absolute_url()约定对与获取不带域名的对象 URL非常理想,但在某些情形下,你可能想显示某个对象带有http://和域名以及所有部分的完整
URL。要完成此工作,你可以使用多站点框架。下面是个简单的例子:
>>> from django.contrib.sites.models import Site >>> obj = MyModel.objects.get(id=3) >>> obj.get_absolute_url() '/mymodel/objects/3/' >>> Site.objects.get_current().domain 'example.com' >>> 'http://%s%s' % (Site.objects.get_current().domain, obj.get_absolute_url())
'http://example.com/mymodel/objects/3/'
当前站点管理器
如果站点在你的应用中扮演很重要的角色,请考虑在你的模型中使用方便的CurrentSiteManager。这是一个模型管理器(见附录B),它会自动过滤使其只包含与当前站点相关联的对象。
通过显示地将CurrentSiteManager加入模型中以使用它。例如:
from django.db import models from django.contrib.sites.models import Site from django.contrib.sites.managers import CurrentSiteManager class Photo(models.Model): photo = models.FileField(upload_to='/home/photos') photographer_name = models.CharField(maxlength=100) pub_date = models.DateField() site = models.ForeignKey(Site) objects = models.Manager() on_site = CurrentSiteManager()
通过该模型,``Photo.objects.all()``将返回数据库中所有的Photo对象,而Photo.on_site_all()仅根据SITE_ID设置返回与当前站点相关联的Photo对象。
换言之,以下两条语句是等效的:
Photo.objects.filter(site=settings.SITE_ID)
Photo.on_site.all()
CurrentSiteManager是如何知道Photo的哪个字段是Site呢?缺省情况下,它会查找一个叫做site的字段。如果模型中有个外键或多对多字段叫做site之外的名字,你必须显示地将它作为参数传递给CurrentSiteManager。下面的模型中有个叫做publish_on的字段,如下所示:
from django.db import models from django.contrib.sites.models import Site from django.contrib.sites.managers import CurrentSiteManager class Photo(models.Model): photo = models.FileField(upload_to='/home/photos') photographer_name = models.CharField(maxlength=100) pub_date = models.DateField() publish_on = models.ForeignKey(Site) objects = models.Manager() on_site = CurrentSiteManager('publish_on')
如果试图使用CurrentSiteManager并传入一个不存在的字段名, Django将引发一个ValueError异常。
注意事项
即便是已经使用了CurrentSiteManager,你也许还想在模型中拥有一个正常的(非站点相关)的管理器。正如在附录
B 中所解释的,如果你手动定义了一个管理器,那么 Django不会为你创建全自动的objects = models.Manager()管理器。
同样,Django的特定部分——即 Django超级管理站点和通用视图——使用的管理器首先在模型中定义,因此如果希望超级管理站点能够访问所有对象(而不是仅仅站点特有对象),请于定义CurrentSiteManager之前在模型中放入objects
= models.Manager()。
Django如何使用多站点框架
尽管并不是必须的,我们还是强烈建议使用多站点框架,因为 Django在几个地方利用了它。即使只用 Django来支持单个网站,你也应该花一点时间用domain和name来创建站点对象,并将SITE_ID设置指向它的
ID 。
以下讲述的是 Django如何使用多站点框架:
§ 在重定向框架中(见后面的重定向一节),每一个重定向对象都与一个特定站点关联。当 Django搜索重定向的时候,它会考虑当前的SITE_ID。
§ 在注册框架中,每个注释都与特定站点相关。每个注释被张贴时,其site被设置为当前的SITE_ID,而当通过适当的模板标签列出注释时,只有当前站点的注释将会显示。
§ 在 flatpages框架中 (参见后面的
Flatpages一节),每个 flatpage都与特定的站点相关联。创建 flatpage时,你都将指定它的site,而
flatpage中间件在获取 flatpage以显示它的过程中,将查看当前的SITE_ID。
§ 在 syndication框架中(参阅Django输出非HTML内容),title和description的模板自动访问变量{{
site }},它就是代表当前着桨的Site对象。Also, the hook for providing item URLs will use thedomainfrom
the currentSiteobject if you dont specify a fully qualified domain.
§ 在身份验证框架(参见Django会话、用户和注册)中,django.contrib.auth.views.login视图将当前Site名称作为{{
site_name }}传递给模板。
Flatpages - 简单页面
尽管通常情况下总是建造和运行数据库驱动的 Web应用,你还是会需要添加一两张一次性的静态页面,例如“关于”页面,或者“隐私策略”页面等等。可以用像
Apache 这样的标准Web服务器来处理这些静态页面,但却会给应用带来一些额外的复杂性,因为你必须操心怎么配置 Apache,还要设置权限让整个团队可以修改编辑这些文件,而且你还不能使用
Django 模板系统来统一这些页面的风格。
这个问题的解决方案是使用位于django.contrib.flatpages开发包中的 Django简单页面(flatpages)应用程序。该应用让你能够通过
Django超级管理站点来管理这些一次性的页面,还可以让你使用 Django模板系统指定它们使用哪个模板。它在后台使用了 Django模型,也就是说它将页面存放在数据库中,你也可以像对待其他数据一样用标准
Django数据库 API
存取简单页面。
简单页面以它们的 URL和站点为键值。当创建简单页面时,你指定它与哪个URL以及和哪个站点相关联。
使用简单页面
安装平页面应用程序必须按照下面的步骤:
1. 添加'django.contrib.flatpages'到INSTALLED_APPS设置。django.contrib.flatpages依赖于django.contrib.sites,所以确保这两个开发包都包括在``INSTALLED_APPS``设置中。
2. 将'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'添加到MIDDLEWARE_CLASSES设置中。
3. 运行manage.py syncdb命令在数据库中创建必需的两个表。
简单页面应用程序在数据库中创建两个表:django_flatpage和django_flatpage_sites。django_flatpage只是将
URL 映射到到标题和一段文本内容。django_flatpage_sites是一个多对多表,用于关联某个简单页面以及一个或多个站点。
该应用所带来的FlatPage模型在django/contrib/flatpages/models.py进行定义,如下所示:
from django.db import models from django.contrib.sites.models import Site class FlatPage(models.Model): url = models.CharField(maxlength=100) title = models.CharField(maxlength=200) content = models.TextField() enable_comments = models.BooleanField() template_name = models.CharField(maxlength=70, blank=True) registration_required = models.BooleanField() sites = models.ManyToManyField(Site)
让我们逐项看看这些字段的含义:
§ url:该简单页面所处的 URL,不包括域名,但是包含前导斜杠
(例如/about/contact/)。
§ title:简单页面的标题。框架不对它作任何特殊处理。由你通过模板来显示它。
§ content:简单页面的内容 (即HTML
页面)。框架不会对它作任何特别处理。由你负责使用模板来显示。
§ enable_comments:是否允许该简单页面使用注释。框架不对此做任何特别处理。你可在模板中检查该值并根据需要显示注释窗体。
§ template_name:用来解析该简单页面的模板名称。这是一个可选项;如果未指定模板或该模板不存在,系统会退而使用默认模板flatpages/default.html。
§ registration_required:是否注册用户才能查看此简单页面。该设置项集成了 Djangos验证/用户框架。
§ sites:该简单页面放置的站点。该项设置集成了 Django多站点框架,该框架在本章的《多站点》一节中有所阐述。
你可以通过 Django超级管理界面或者 Django数据库 API
来创建平页面。要了解更多内容,请查阅《添加、修改和删除简单页面》一节。
一旦简单页面创建完成,FlatpageFallbackMiddleware将完成(剩下)所有的工作。每当 Django引发
404 错误,作为终极手段,该中间件将根据所请求的 URL检查平页面数据库。确切地说,它将使用所指定的 URL以及SITE_ID设置对应的站点
ID查找一个简单页面。
如果找到一个匹配项,它将载入该简单页面的模板(如果没有指定的话,将使用默认模板flatpages/default.html)。同时,它把一个简单的上下文变量——flatpage(一个简单页面对象)传递给模板。在模板解析过程中,它实际用的是RequestContext。
如果FlatpageFallbackMiddleware没有找到匹配项,该请求继续如常处理。
注意
该中间件仅在发生 404(页面未找到)错误时被激活,而不会在 500(服务器错误)或其他错误响应时被激活。还要注意的是必须考虑MIDDLEWARE_CLASSES的顺序问题。通常,你可以把FlatpageFallbackMiddleware放在列表最后,因为它是一种终极手段。
添加、修改和删除简单页面
可以用两种方式增加、变更或删除简单页面:
通过超级管理界面
如果已经激活了自动的 Django超级管理界面,你将会在超级管理页面的首页看到有个 Flatpages区域。你可以像编辑系统中其它对象那样编辑简单页面。
通过 Python API
前面已经提到,简单页面表现为django/contrib/flatpages/models.py中的标准 Django模型。因此,你可以通过
Django数据库 API
来存取简单页面对象,例如:
>>> from django.contrib.flatpages.models import FlatPage >>> from django.contrib.sites.models import Site >>> fp = FlatPage( ... url='/about/', ... title='About', ... content='<p>About this site...</p>', ... enable_comments=False, ... template_name='', ... registration_required=False, ... ) >>> fp.save() >>> fp.sites.add(Site.objects.get(id=1)) >>> FlatPage.objects.get(url='/about/') <FlatPage: /about/ -- About>
使用简单页面模板
缺省情况下,系统使用模板flatpages/default.html来解析简单页面,但你也可以通过设定FlatPage对象的template_name字段来覆盖特定简单页面的模板。
你必须自己创建flatpages/default.html模板。只需要在模板目录创建一个flatpages目录,并把default.html文件置于其中。
简单页面模板只接受有一个上下文变量——flatpage,也就是该简单页面对象。
以下是一个flatpages/default.html模板范例:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html> <head> <title>{{ flatpage.title }}</title> </head> <body> {{ flatpage.content }} </body> </html>
重定向
通过将重定向存储在数据库中并将其视为 Django模型对象,Django
重定向框架让你能够轻松地管理它们。比如说,你可以通过重定向框架告诉Django,把任何指向/music/的请求重定向到/sections/arts/music/。当你需要在站点中移动一些东西时,这项功能就派上用场了——网站开发者应该穷尽一切办法避免出现坏链接。
使用重定向框架
安装重定向应用程序必须遵循以下步骤:
1. 将'django.contrib.redirects'添加到INSTALLED_APPS设置中。
2. 将'django.contrib.redirects.middleware.RedirectFallbackMiddleware'添加到MIDDLEWARE_CLASSES设置中。
3. 运行manage.py syncdb命令将所需的表安装到数据库中。
manage.py syncdb在数据库中创建了一个django_redirect表。这是一个简单的查询表,只有site_id、old_path和new_path三个字段。
你可以通过 Django超级管理界面或者 Django数据库 API
来创建重定向。要了解更多信息,请参阅《增加、变更和删除重定向》一节。
一旦创建了重定向,RedirectFallbackMiddleware类将完成所有的工作。每当 Django应用引发一个
404 错误,作为终极手段,该中间件将为所请求的 URL在重定向数据库中进行查找。确切地说,它将使用给定的old_path以及SITE_ID设置对应的站点
ID查找重定向设置。(查阅前面的《多站点》一节可了解关于SITE_ID和多站点框架的更多细节)然后,它将执行以下两个步骤:
§ 如果找到了匹配项,并且new_path非空,它将重定向到new_path。
§ 如果找到了匹配项,但new_path为空,它将发送一个 410 (Gone) HTTP头信息以及一个空(无内容)响应。
§ 如果未找到匹配项,该请求将如常处理。
该中间件仅为 404错误激活,而不会为 500
错误或其他任何状态码的响应所激活。
注意必须考虑MIDDLEWARE_CLASSES的顺序。通常,你可以将RedirectFallbackMiddleware放置在列表的最后,因为它是一种终极手段。
注意
如果同时使用重定向和简单页面回退中间件,必须考虑先检查其中的哪一个(重定向或简单页面)。我们建议将简单页面放在重定向之前(因此将简单页面中间件放置在重定向中间件之前),但你可能有不同想法。
增加、变更和删除重定向
你可以两种方式增加、变更和删除重定向:
通过超级管理界面
如果已经激活了全自动的 Django超级管理界面,你应该能够在超级管理首页看到重定向区域。可以像编辑系统中其它对象一样编辑重定向。
通过 Python API
django/contrib/redirects/models.py中的一个标准 Django模型代表了重定向。因此,你可以通过 Django数据库
API 来存取重定向对象,例如:
>>> from django.contrib.redirects.models import Redirect >>> from django.contrib.sites.models import Site >>> red = Redirect( ... site=Site.objects.get(id=1), ... old_path='/music/', ... new_path='/sections/arts/music/', ... ) >>> red.save() >>> Redirect.objects.get(old_path='/music/') <Redirect: /music/ ---> /sections/arts/music/>
CSRF 防护
django.contrib.csrf开发包能够防止遭受跨站请求伪造攻击 (CSRF).
CSRF, 又叫进程跳转,是一种网站安全攻击技术。当某个恶意网站在用户未察觉的情况下将其从一个已经通过身份验证的站点诱骗至一个新的 URL时,这种攻击就发生了,因此它可以利用用户已经通过身份验证的状态。开始的时候,要理解这种攻击技术比较困难,因此我们在本节将使用两个例子来说明。
一个简单的 CSRF例子
假定你已经登录到example.com的网页邮件账号。该网页邮件站点上有一个登出按钮指向了 URLexample.com/logout,换句话说,要登出的话,需要做的唯一动作就是访问
URL :example.com/logout。
通过在(恶意)网页上用隐藏一个指向 URLexample.com/logout的d5ba1642137c3f32f4f4493ae923989c,恶意网站可以强迫你访问该
URL 。因此,如果你登录example.com的网页邮件账号之后,访问了带有指向example.com/logout之d5ba1642137c3f32f4f4493ae923989c的恶意站点,访问该恶意页面的动作将使你登出example.com。
诚然,对你而言登出一个网页邮件站点并不会构成多大的安全破坏,但同样的攻击可能发生在任何信任用户的站点之上,比如在线银行网站或者电子商务网站。
稍微复杂一点的CSRF例子
前の例では、example.com が HTTP GET メソッド経由でステータスの変更 (つまり、ログインとログアウト) を許可しているため、部分的に責任があります。サーバー状態の変更に
HTTPPOST メソッドが必要な場合、状況はさらに良くなります。ただし、状態変更操作に POST メソッドの使用が必須であっても、依然として CSRF 攻撃に対して脆弱です。
example.com がログアウト機能をアップグレードしたとします。ff9c23ada1bcecdd1a0fb5d5a0f18437 ボタンのログアウトは、URLexample.com/logout を指す POST アクションによって完了します。 ff9c23ada1bcecdd1a0fb5d5a0f18437 フィールド:
1e7df074b3cd0e2460b69627e263070a
これにより、example.com/logout への単純な POST によってユーザーがログアウトされなくなります。ログアウトするには、ユーザーは POST リクエストを example.com/logout に送信し、値が「true」の POST 変数を送信する必要があります。
追加のセキュリティメカニズムを追加したにもかかわらず、この設計は依然として CSRF 攻撃に対して脆弱です。悪意のあるページには少しの改善が必要なだけです。攻撃者はフォーム全体をサイトのターゲットにし、非表示の d5ba1642137c3f32f4f4493ae923989c 内に隠し、
JavaScript を使用してフォームを自動的に送信する可能性があります。
CSRFの防止
では、この攻撃からサイトを安全にすることは可能でしょうか?最初のステップは、すべての GET メソッドに副作用がないことを確認することです。こうすることで、悪意のあるサイトにあなたのページが d5ba1642137c3f32f4f4493ae923989c として含まれていても、悪影響が及ぶことはありません。
この手法では、POST リクエストは考慮されません。 2 番目のステップは、すべての POST