iOS 保存領域 比較 〜NSUserDefaults, CoreData,Keychain,等〜
アプリ容量の制限について
アプリのストレージの最大容量は、おそらく制限なし(20M以上だとDL時にwifiが必要となる)
保存領域について
種類 | 説明 |
---|---|
アプリ領域 | アプリが自由に使え、他のアプリから参照できない領域 |
OS領域 | OSが使用するデータが保存されており、基本的にアプリから参照できない領域 |
共有領域 | メディアライブラリ(写真、音楽等)やイベント(カレンダー等)、連絡先は全てのアプリから参照することができる領域(アプリからできる内容は、書き込みと閲覧のみ) |
永続データの復元(比較)について
- 復元時にデータも復元するには、外部サーバーにデータを保存するかiCloudを利用しないと殆ど初期化される
- 保存領域が共有領域であれば、アプリ自体にiCloudの処理をいれなくても、共有領域のデータは復元できそう
種類 | 復元可否 | 備考 |
---|---|---|
アーカイブ | ☓ | オブジェクトをバイナリ形式に変換してからファイルに永続化(一番基礎的な技術) |
プロパティリスト | - | ユーザーが変更しないもの(定数)、plist |
NSUserDefaults | ☓ | ユーザーが変更するもの |
CoreData | ☓ | データ量多い |
Keychain | ◯ | パスワードや個人情報(暗号化されていない場合復元されない) |
アプリ内にDBとして保存したい場合realmがオススメです。(日本語のドキュメントもしっかりしていて、組み込みも簡単で、ブラウザも用意されています)
参考URL
Xcode デバッグ 値変更
概要
- Xcodeでデバック中に、変数の値や関数の実行する方法の説明です
- デバックエリアでLLDBコマンドを実行して確認します
- 値の変更はデバックエリアで「po 変数名 = 値」で変更できます
LLDBとは、Mac OS X上のXcodeでデフォルトのデバッガで、デスクトップとiOSデバイスとシミュレータ上のC、のObjective-CとC++、Swiftのデバッグをサポートしています。
- 参考URL:About LLDB and Xcode
実行方法
- デバックエリアを表示(メニューバー->View->Debug Area->Show Debug Area)
- 止めたいところにブレークポイント設定
- プログラムの実行
- デバックエリアでLLDBコマンドの実行
実行例
- 変数の値や関数の実行する場合は、「(lldb) po 変数名 または 関数名」を実行します
「po self.dynamicType」でプロジェクト名とクラス名を表示している例
LLDBコマンド一覧(一部抜粋)
省略形 | 内容 | 正式名 |
---|---|---|
po | 式の評価(poはオブジェクト、pは基本型も可) | expression -o -- |
h | ヘルプの表示 | help |
s | ステップイン | thread step-in |
c | 続きを実行(次のbreakPointまで) | process continue |
br l | breakPointを一覧表示 | breakpoint list |
bt | 現スレッドのバックトレース情報を表示 | thread backtrace |
iOS AdMob 実装 方法
AdMob SDKの導入方法や手順が書いてあります。(iPhoneやiPadが対象)
やりたいこと
iOSアプリにAdMobのバナー広告を表示させたい。
(AdMobとは、Googleが運営するアプリ専門のアドネットワーク。日本国内の収益性は微妙だが、海外の広告案件が豊富。)
流れ
- AdMobに登録
- アプリにSDK組込
- 広告ユニットIDの作成
細かい部分はAdMobのスターターズガイドがしっかりしているので、その通り進めば多分大丈夫だと思います。
1. AdMobに登録
AdMobのサイトでユーザー登録をします。
「Sign Up」ボタン押下後適当に個人情報を入力して進みます。
(全文英語ですが、一般的なユーザー登録なので英語読めなくても何とかなるはず。。。)
2. アプリにSDK組込
今回は、CocoaPodsを利用してSDKを組込みます。
CocoaPodsの利用方法がわからない方はこちら。
2-1. Podfileの作成・SDKインストール
ターミナルを起動しPodfileを作成しSDKをインストールします。
$ vi Podfile # Podfileの作成 platform :ios, '8.0' pod 'Google-Mobile-Ads-SDK', '~> 7.0' $ pod install # SDKインストール Analyzing dependencies CocoaPods 0.36.0.beta.2 is available. To update use: `gem install cocoapods --pre` [!] This is a test version we'd love you to try. For more information see http://blog.cocoapods.org and the CHANGELOG for this version http://git.io/BaH8pQ. Downloading dependencies Installing Google-Mobile-Ads-SDK (7.0.0) Generating Pods project Integrating client project [!] From now on use `BannerExample.xcworkspace`. $ # 「プロジェクト名.xcworkspace」が作成されていればインストール完了!
2-2. AdMobを実装
とりあえずSDKが読み込めているか試してみましょう
# swift // Bridging-Header.h @import GoogleMobileAds // BannerExampleViewController.swift class BannerExampleViewController { override func viewDidLoad() { super.viewDidLoad() println("Google Mobile Ads SDK version:\(GADRequest.sdkVersion())") } }
# Objective-C // BannerExampleViewController.m @import GoogleMobileAds; #import "ViewController.h" @implementation BannerExampleViewController - (void)viewDidLoad { [super viewDidLoad]; NSLog(@"Google Mobile Ads SDK version: %@", [GADRequest sdkVersion]); } @end
次に、バナーを作成し画面表示されたらOKです。
広告ユニットIDは、「3. 広告ユニットIDの作成」で作成し自分のIDに変更しましょう
# swift // BannerExampleViewController.swift // GADBannerViewDelegateを追加 class BannerExampleViewController, GADBannerViewDelegate { override func viewDidLoad() { super.viewDidLoad() println("Google Mobile Ads SDK version:\(GADRequest.sdkVersion())") var bannerView: GADBannerView = GADBannerView() bannerView = GADBannerView(adSize:kGADAdSizeBanner) // 広告ユニットIDを指定する(3. 広告ユニットIDの作成参照) bannerView.adUnitID = "ca-app-pub-3940256099942544/2934735716" bannerView.delegate = self bannerView.rootViewController = self self.view.addSubview(bannerView) bannerView.loadRequest(GADRequest()) } }
# Objective-C // BannerExampleViewController.h // SDK から GADBannerView の定義をインポートする #import "GADBannerView.h" @interface BannerExampleViewController : UIViewController { // インスタンス変数として 1 つ宣言する GADBannerView *bannerView_; } @end // BannerExampleViewController.m #import "BannerExampleViewController.h" @implementation BannerExampleViewController - (void)viewDidLoad { [super viewDidLoad]; // 画面上部に標準サイズのビューを作成する // 利用可能な広告サイズの定数値は GADAdSize.h で説明されている bannerView_ = [[GADBannerView alloc] initWithAdSize:kGADAdSizeBanner]; // 広告ユニットIDを指定する(3. 広告ユニットIDの作成参照) bannerView_.adUnitID = MY_BANNER_UNIT_ID; // ユーザーに広告を表示した場所に後で復元する UIViewController をランタイムに知らせて // ビュー階層に追加する bannerView_.rootViewController = self; [self.view addSubview:bannerView_]; // 一般的なリクエストを行って広告を読み込む [bannerView_ loadRequest:[GADRequest request]]; } - (void)dealloc { // プロジェクトで ARC を使用している場合は bannerView_ を解放しない [bannerView_ release]; [super dealloc]; } @end
広告のサイズやターゲットの設定はこちら
3. 広告ユニットIDの作成
- AdMob管理画面に「1. AdMobに登録」で作成したアカウントでログイン
- 広告ユニットIDの作成
- 「収益化->新しいアプリの収益化」ページまで移動
- アプリを追加
- 広告フォーマットの選択と広告ユニット名の設定
- 表示の設定方法
- 完了ボタン押下
- 広告ユニットIDをアプリに設定
- 「収益化->すべてのアプリ」ページまで移動
自分が追加したアプリの「広告ID」が発行されています
「2-2. AdMobを実装」で設定していた「広告ID」を自分の「広告ID」に変更しましょう
- 表示と計測の確認
- アプリを起動し広告が表示されているか確認しましょう(広告IDを変更した場合、よく見かける一般的な広告に差し替わっていると思います。)
- 広告を押下し外部サービスが起動することを確認しましょう
- 管理画面上で計測が正しくとれていることを確認しましょう(リアルタイムに反映されないので時間を置いて確認しましょう(私の場合1時間位で反映されていました))
以上で全て終了です。お疲れ様でした!
CoreData の 関連図 と 手順
概要
- CoreDataの知識が曖昧だったのでクラスの関連図や手順についてのメモです(細かい実装方法や使い方は他の参考サイトを参考にしてください)
- DB自体初めての人には登場人物が多くて難易度高めな気がします
- 参考資料:iOS Core Data チュートリアル
関連図
Core Data | RDB |
---|---|
NSManagedObjectModel | DB |
Entity | Table |
NSManagedObject | Record |
Attribute | Column |
- NSManagedObjectModel:管理オブジェクトモデル(データベース)
- NSManagedObject:管理オブジェクト(レコード)
- NSManagedObjectContext:管理オブジェクトコンテキスト(オブジェクト空間、管理オブジェクトのコレクションを管理する)
- NSPersistentStoreCoordinator:永続ストアコーディネータ(ストアを管理して、管理オブジェクトコンテキストに、1つ の統一されたストアのファサードを提供する)
作成手順
- プロジェクトの準備
- テーブルの作成
- テーブルにデータ追加
- NSManagedObjectContextでDML操作(レコードの追加・検索・更新・削除など)
- NSEntityDescriptionでテーブルの指定
- NSManagedObjectで値の受け渡し
- テーブルからデータ取得
- NSManagedObjectContextでDML操作(レコードの追加・検索・更新・削除など)
- NSFetchRequestでテーブルの指定
import CoreData class MyClass : UIViewController { // 3. テーブルにデータ追加 @IBAction func insert(sender: AnyObject) { let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate let managedContext: NSManagedObjectContext = appDelegate.managedObjectContext! /* Create new ManagedObject */ let entity = NSEntityDescription.entityForName("テーブル名", inManagedObjectContext: managedContext) let personObject = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: managedContext) // let myPersonObject = NSManagedObjectを継承したカスタムクラス(entity: entity!, insertIntoManagedObjectContext: managedContext) // 自分で作成したカスタムを使う場合 /* Set the name attribute using key-value coding */ personObject.setValue("値", forKey: "カラム名") // myPersonObject.カラム名 = "値" // 自分で作成したカスタムを使う場合 /* Error handling */ var error: NSError? if !managedContext.save(&error) { println("Could not save \(error), \(error?.userInfo)") } println("object saved") } // 4. テーブルからデータ取得 @IBAction func selectAll(sender: AnyObject) { /* Get ManagedObjectContext from AppDelegate */ let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate let manageContext:NSManagedObjectContext = appDelegate.managedObjectContext! /* Set search conditions */ let fetchRequest = NSFetchRequest(entityName: "テーブル名") var error: NSError? /* Get result array from ManagedObjectContext */ let fetchResults = manageContext.executeFetchRequest(fetchRequest, error: &error) if let results: Array = fetchResults { for obj:AnyObject in results { let name:String? = obj.valueForKey("カラム名") as? String println(name) } println(results.count) } else { println("Could not fetch \(error) , \(error!.userInfo)") } }
まとめ
- xcdatamodeldでテーブル情報(テーブル名、カラム名、カラム属性、リレーション等)を設定する
- NSManagedObjectを継承したクラスをテーブルのカラム名を書いて、テーブルとの受け渡し用の入れ物に使う
- NSManagedObjectContextでDML操作(レコードの追加・検索・更新・削除など)
- NSEntityDescriptionでテーブルの指定
- データの保存場所を確認したい場合、applicationDocumentsDirectoryを見る
- 個人的には、FMDBのライブラリを使って直接SQLでデータをやりとりした方が自由で楽なきがします。。。
参考サイト
Swift 文法
概要
Swiftの入門的な部分をまとめてみました。
初心者向けに文法の部分を中心に書きました。
また、SwiftはObjective-Cより直感的ですっきりしているので、
iOSの開発を始めたい方はSwiftでの開発がオススメです。
目次
- 変数宣言(var,let)
- 型(Int,String...)
- 文字列操作
- 配列(Array)
- 辞書(Dictionary)
- タプル(Tuple)
- 繰り返し(for,while)
- 条件分岐(if,switch)
- 関数(func)
- クラスと構造体(class,struct)
- オプショナル型(Optional)
- おすすめの勉強法
1. 変数宣言(var,let)
- 変数の宣言には「var」と「let」がある
- varは、変数
- letは、定数
// 型を指定して生成 var varInt:Int // 宣言:var 変数名:型 varInt = 0 // 代入:変数名=値 var varInt2:Int = 0 // 宣言+代入:var 変数名:型=値 // 型推論(型を指定せずに生成) var varInt3 = 3 // 値からInt型の変数が生成される let pi = 3.14 // let pi2:Double // 宣言のみだとエラー(値の代入も宣言時に必要) // pi = 3.1415926 // 値を変更するとエラー
2. 型(Int,String...)
- マイナス値がない「UInt」型がある
- 添字などで使うと安全性が高まる
- 型指定なしの「Any」型がある
名前 | 説明 |
---|---|
Int | 整数型 |
UInt | 符号なし整数型 |
Float | 単精度少数型 |
Double | 倍精度少数型 |
Bool | 論理型 |
Charecter | 文字型 |
String | 文字列型 |
Any | 型指定なし |
AnyObject | 型指定なし(Classに限定) |
3. 文字列操作
- Stringの文字列は「+」で結合できる
- var str:String = "Hello"+"World"
- String内に処理の実行結果を代入する時は「\(処理)」で出来る
- var str:String = "1+1=\(1+1)"
4. 配列(Array)
// 型を指定 var varArray:[String] // 型推論(型を指定せずに生成) var varArray2 = ["var0", "var1", "var2"] // 空の配列 var varArray3 = [String]() var varArray4:[String] = Array() // 要素の追加 varArray2.append("var3")
5. 辞書(Dictionary)
// 型を指定 var varDict:Dictionary<String, String> // 型推論(型を指定せずに生成) var varDict2 = ["key0":"var0", "key1":"var1", "key2":"var2"] // 空の辞書 var varDict3 = Dictionary<String, String>() var varDict4:Dictionary<String, String> = Dictionary() // 要素の追加 varDict2["key3"] = "var3" // 値の取得 if let value = varDict2["key3"] { print(value) } // キーと値の取得例 for (dictName, dictValue) in varDict2 { print("キーは\(dictName)で、値は\(dictValue)です。") }
6. タプル(Tuple)
- タプルは複数の値をグループとして扱える(「x,y,width,height」や「name,age」とかをグループにしたい時に便利)
- タプル内の値は、異なる型の値を設定出来る
// 値を設定 let http404Error = (404, "Not Found") // 値を取得 print("The status code is \(http404Error.0)") // 他の変数に設定、取得 let (statusCode, statusMessage) = http404Error print("The status code is \(statusCode)") // 変数名をつけて設定、取得 let http200Status = (statusCode: 200, description: "OK") print("The status code is \(http200Status.statusCode)")
7. 繰り返し(for,while)
var roopArray = ["var0","var1","var2"] // for-in ループ for i in 0..<roopArray.count { print(roopArray[i]) } // 条件文付きforループ for var i = 0; i < roopArray.count; i++ { print(roopArray[i]) } // 代入forループ for value in roopArray { print("\(value)") } // while var roopIndex = 0 while roopIndex < roopArray.count { print(roopArray[roopIndex]) roopIndex++ }
8. 条件分岐(if,switch)
- ifの条件式に()は、書かなくてもOK
- switchは、breakを書かなくてもbreakする
- switchでも、条件が複数指定できる
- switchでも、細かい条件も指定できる
var varStr:String = "Hello World" // if if varStr != "" { print(varStr) } // if条件分岐+代入 var varStr2:String? if let varStr3 = varStr2 { print(varStr3) // varStr2がnilじゃない場合、varStr3にvarStr2を代入 } else { print("varStr2 is nil") } // if条件分岐+代入(三項演算子) その2 var varInt:Int = 1 var varStr3:String = (1 == varInt) ? "1" : "" // switch let vegetable = "red pepper" switch vegetable { case "celery": let vegetableComment = "Add some raisins and make ants on a log." case "cucumber", "watercress": // 複数条件 let vegetableComment = "That would make a good tea sandwich." case let x where x.hasSuffix("pepper"): // 細かい条件 let vegetableComment = "Is it a spicy \(x)?" default: let vegetableComment = "Everything tastes good in soup." }
9. 関数(func)
- 戻り値が複数設定出来る
// 関数を呼び出す // func 関数名(引数: 型) -> 戻り値 func square(num: Int) -> Int { return num * num } // 関数を使う print("計算結果は\(square(2))です") // 引数が複数の場合、引数を「,」区切りで宣言 func sum(num1: Int, num2: Int) -> Int { return num1 + num2 } // 戻りが複数の場合、戻り値を「,」区切りで宣言 func tax(num: Float) -> (five: Float, eight: Float) { return (five: num*1.05, eight: num*1.08) } var taxPrice = tax(100.0) // 複数の戻りを取り出す場合、「.」で指定する print("消費税5%だと、\(taxPrice.five)円です") print("消費税8%だと、\(taxPrice.eight)円です")
10. クラスと構造体(class,struct)
- クラスと構造体は、変数や関数等をまとめたプログラム
- クラスと構造体の違いは、「型」として性質(セマンティクス)が異なる
- クラス(class):参照型(コピーした時に、コピー元の値を変更するとコピー先も同じものを参照しているので値が変わる)
- 構造体(struct):値型(コピーしてもそれぞれにインスタンスされる)
クラス(class)
- 本格的に作る場合に向いている
- 継承を使える
- ARCが使える
- インスタンスのライフサイクルが長いもに向いている
- 例)UIColor,UIImage等(殆どがクラス)
class RectClass { // 変数 var x: Float = 0 var y: Float = 0 var width: Float = 0 var height: Float = 0 // 関数 func maxX() -> Float { return x+width } func maxY() -> Float { return y+height } } var rectC = RectClass() rectC.x = 0; rectC.width = 100; print("最大幅は\(rectC.maxX())です");
構造体(struct)
- 軽く作る場合に向いている
- プロパティ数が少ないもに向いている
- 例)CGRext,CGPoint,CGSize,Array等
struct RectStruct { // 変数 var x: Float = 0 var y: Float = 0 var width: Float = 0 var height: Float = 0 // 関数 func maxX() -> Float { return x+width } func maxY() -> Float { return y+height } } var rect0 = RectStruct() var rect1 = RectStruct(x:0.0, y:0.0, width:100.0,height:200.0) print("最大幅は\(rect1.maxX())です");
11. オプショナル型(Optional)
- オプショナル型
- 「?」をつける
- nilになる可能性があることを示している
- ラップされた状態
- 非オプショナル型
- 「!」をつける
- nilになる可能性がないことを示している
- アンラップされた状態
- オプショナル連鎖
- オプショナル型のメソッドや変数にアクセスする時は、オプショナルでアクセスする必要がある
// NSURLクラスでの例 NSURL.h var scheme: String? { get } var host: String? { get } var path: String? { get } var query: String? { get } MyClass var url = NSURL(string: "http://www.apple.com")! var host: String? = url.host // ラップ var host2: String = url.host! // アンラップ // var host3: String = url.query // クエリーを設定していないので実行時エラーになる(宣言元にラップ状態の明示が必要) var host3: String? = url.query // host.hasPrefix("www") // オプショナル連鎖(?が必要) host?.hasPrefix("www")
12. おすすめの勉強法
- Swift - Overview - Apple Developerをざっくり読む
- The Swift Programing Languageを読む(Swift言語ブログが日本語で解説されていて分かりやすい)
- A Swift Tour: Playground fileからSampleCodeをダウンロード(1.The Swift Programing Languageがplaygroundで閲覧出来る)
- 気になる点をplaygroundで書く
- Using Swift with Cocoa and Objective-Cを読む(CocoaのObjective-Cとswiftの違いが書かれている)
- オススメの書籍
文法もわかりやすく、rssアプリを作成出来ます。基礎の入門書としてオススメです。
他に参考になる資料(2015.12追記)
- Swift.org(Swiftのコミュニティーのためのポータルサイト)
- Swift2 メモ(try catch、Availability、guard、defer等)
- プログラミング言語 Swift(はてな教科書)←初心者にかなりオススメ
- Swift での iOS アプリ開発(はてな教科書)←初心者にかなりオススメ
- Awesome-Swift-Education(Swiftを学習するためのキュレーションリスト)
その他
- IBM Swift Sandbox(swiftがウェブプラザで動かせるサイト。マックを持ってないけど軽く触ってみたい人にオススメ。)
Web系 システム 開発 セキュリティ 備忘録
概要
ウェブアプリケーションのセキュリティの基礎や入門的な部分をざっくり書き出しました。
ログイン機能を使う場合やインフラよりのセキュリティは書いていません。
1. 脆弱性の原因と攻撃パターン
脆弱性が生まれる理由
- バグによるもの
- チェック機能不足によるもの
- ディレクトリ・トラバーサル等
能動的攻撃と受動的攻撃
- 能動的攻撃:攻撃者がwebサーバーに対して直接攻撃すること(SQLインジェクション等)
- 受動的攻撃:攻撃者がwebサーバーに対して直接攻撃せず、Webサイトの利用者に罠を仕掛ける
- 単純な受動的攻撃:ユーザーが怪しいサイト閲覧してウィルスに感染
- 正規サイトを悪用する受動的攻撃:攻撃者がwebサーバーに対して不正操作をして、ユーザーが正規サイトに訪れるとウィルスに感染
- サイトをまたがった受動的攻撃:①ユーザーが罠サイトを閲覧②ユーザーが正規サイトに攻撃のリクエスト送信③正規サイトからJSなどの仕掛けを含むレスポンスが返却
2. 脆弱性一覧
- 脆弱性には出力に起因するものと処理に起因するものがある
- 出力に起因する脆弱性には「インジェクション」という単語がつくものが多い
- インジェクションとは、「"」「'」等の終端マークを混入させ処理や出力の構造を変化させる行為
# 処理の流れ クライアントブラウザ ↓ HTTPリクエスト サーバー(入力値検証→処理→出力) ↓ HTTPレスポンス クライアントブラウザ
機能と脆弱性の対応
処理 | 出力 | 対応アプリ | 発生する脆弱性の種類 | 起因する脆弱性 |
---|---|---|---|---|
処理 | 表示/HTML | ブラウザ | クロスサイト・スクリプティング HTTPヘッダ・インジェクション |
出力 |
処理 | DB/SQL | RDB | SQLインジェクション | 出力 |
処理 | 外部コマンド | シェル | OSコマンド・インジェクション | 出力 |
処理 | メール | メール | メールヘッダ・インジェクション | 出力 |
処理 | ファイル | ファイル | ディレクトリ・トラバーサル | 処理 |
重要な処理 | - | - | クロスサイト・リクエストフォージェリ | 処理 |
認証 | - | - | セッションフィクセーション | 処理 |
許可 | - | - | 認可不備 | 処理 |
インジェクション系脆弱性
脆弱性名 | インターフェース | 悪用手口 | データの終端 |
---|---|---|---|
クロスサイト・スクリプティング | HTML | JS等の注入 | < " など |
HTTPヘッダ・インジェクション | HTTP | HTTPレスポンスヘッダの注入 | 改行 |
SQLインジェクション | SQL | SQL命令文注入 | ' など |
OSコマンド・インジェクション | シェルスクリプト | コマンドの注入 | ; | など |
メールヘッダ・インジェクション | sendmailコマンド | メールヘッダ、本文の注入・改変 | 改行 |
3. 入力処理の脆弱性
- Webアプリケーションの入力には、HTTPリクエストとしてわたらせるパラメータ(GET,POST,cookieなど)がある
- 入力処理には次の3つの検証・変換が必要
クライアントブラウザ ↓ HTTPリクエスト ----- |入力| ←ここ |処理| |出力| ----- ↓ HTTPレスポンス クライアントブラウザ
文字エンコーディングの変換
言語 | 自動変換 | スクリプト記述 |
---|---|---|
PHP | pjp.iniなど | mb_convert_encoding |
Perl | - | Encode::decode |
Java | setCharacterEncoding | Stringクラス |
ASP.NET | web.config | - |
4. 表示処理の脆弱性
- 表示処理が原因で発生する問題には次の2つがある
- クロスサイト・スクリプティング
- エラーメッセージからの情報漏洩
クライアントブラウザ ↓ HTTPリクエスト ----- |入力| |処理| |出力| ----- ↓ HTTPレスポンス クライアントブラウザ ←ここ
クロスサイト・スクリプティング
- クロスサイトスクリプティングとは、ウェブページの部分をユーザからの入力をそのままエコーバック(おうむ返し)することによって生成しているアプリケーションのセキュリティ上の不備を利用して、サイト間を横断して悪意のあるスクリプトを注入する攻撃のこと
- クロスサイト・スクリプティングではユーザーに対して次のような被害がある
クロスサイト・スクリプティング対策
- 必須対策(個別):HTMLの要素内容について[<]や[&]をエスケープする、属性値は「"」で囲い[<]や[&]をエスケープする
- 必須対策(共通):HTTPレスポンスに文字エンコーディングを明示する
- 保険的対策:入力値検証、クッキーにHttpOnly属性を付与、TRACEメソッドを無効
5. SQLの脆弱性
- SQLの脆弱性にはSQLインジェクションがある
- SQLインジェクションとは、SQLの呼び出しに不備がある場合発生する脆弱性でSQLが開発者の意図しない形にに改変され実行されること
- SQLインジェクションに脆弱性がある場合、次のような影響を受ける可能性がある
- データベース内の全ての情報が盗まれる
- データベースの内容が書き換えられる
- 承認を回避される(IDとパスワードを用いずにログインされる)
- その他、データーバースサーバー上のファイルの読み書き、プログラム実行などが行われる
クライアントブラウザ ↓ HTTPリクエスト ----- |入力| |処理| |出力| ←ここ ----- ↓ HTTPレスポンス クライアントブラウザ
脆弱性が生まれる原因
文字列リテラル
# 文字列リテラルの例(BOOKSテーブルから著者がO'Reillyのデータを取得) SELECT * FROM BOOKS WHERE AUTHOR = 'O'Reilly' 'O'が文字列リテラルで、Reilly'が文字れるリテラルからはみ出した部分 (「SELECT * FROM BOOKS WHERE AUTHOR = 'O';DELETE FROM BOOKS;」みたいな感じで悪用できる)
数値リテラル
# 数値リテラルの例 SELECT * FROM BOOKS WHERE MONEY <= 20000;DELETE FROM BOOKS 20000が数値リテラル、それ以降が脆弱性を含む意図しない形のSQL
対策
6. まとめ
- 入力値のインジェクション対策はかならず行う(「"」「'」「;」等を含んだ入力値でテストする)
- HTMLの要素内容について[<]や[&]をエスケープする
- HTTPレスポンスに文字エンコーディングを明示する
- 入力値検証、クッキーにHttpOnly属性を付与、TRACEメソッドを無効
- おすすめ書籍
セキュリティの事がひと通り書いてあり、サイトを作成する前に考慮すべき考え事がわかりやすく書いてあります。
iOS SNS シェア 実装方法
概要
SNS等のシェア機能の実装方法の説明です。
実装内容はシェアボタン押下時にアクションシートを表示し、シェアしたい内容を選びシェアするような実装内容です。
Webページを表示しシェアしていますが、それ以外でも同じような実装方法でシェア可能です。
シェア先は次の6個!(mixiは登記簿謄本が必要な為今回は対象外としました)
実装結果イメージ(「safari」と「URLをコピー」はおまけ)
この記事では、「UIApplication sharedApplication」で実装しています。
iOS6移行はUIActivityViewControllerで実装するのが楽だと思うので、iOS6移行は他の記事を参考にする方がお勧めです!
前提条件
TWITTER、FACEBOOK(iOS6以上)
Xcodeのライブラリーに「Social.framework」を追加する必要があります。
(TARGETS->MyApp->Build Phases->Link Binary With Libraries)
Google+、EverNote
Google+とEverNoteは、SDKやサービスの登録が必要となります。
(EverNoteはサービス登録が必要になるので、少し時間と手間がかかります)
簡単な概要は次の通りです。
Google+
名称 | Google+ Platform |
---|---|
URL | https://developers.google.com/+/ |
必要なライブラリ | Security.framework,SystemConfiguration.framework |
手順 | 1. APIs Console プロジェクトを作成する2. SDK をダウンロードして、サンプル アプリを実行する3. SDK のダウンロードとインストール4. プログラム修正 |
参考URL | スタートガイド(iOS)Googleでログインインタラクティブな投稿の作成 |
備考 | 開発用と製品用でIDが異なる。使えないhtmlタグがある。 |
EverNote
名称 | EverNote Developers |
---|---|
URL | https://dev.evernote.com/intl/jp/ |
必要なライブラリ | Security.framework |
手順 | 1.サンドボックスのアカウント登録(アプリごとで共通のアカウントを利用する)2. Evernote API キー取得(アプリごと別々に取得)3. SDK のダウンロードとインストール4.プログラム修正・サンドボックス上のEverNoteでテスト5.サンドボックスからプロダクションに変更依頼(アクティベーション)6.プログラム修正(プロダクションに変更(ホストを「BootstrapServerBaseURLStringSandbox」→「BootstrapServerBaseURLStringUS」)) |
参考URL | スタートガイドサンドボックスアクティベーション(プロダクションAPIキーの有効化)よくある質問 |
備考 | 開発用と製品用でIDが異なる。使えないhtmlタグがある。アクティベーションの申請時にはgoogleアカウントは使用できないので個人名・個人用メールアドレスで依頼アクティベーションの承認待ち時間は約2〜3日 |
プログラム
流れ
- アクションシートの生成
- アクションシートの表示(シェアボタン押下時)
- アクションシートの選択した内容でシェア
サンプルコード
#import <Social/Social.h> #import "EvernoteSession.h" #import "EvernoteUserStore.h" #import "EvernoteNoteStore.h" #import "GTLPlusConstants.h" #import "GPPShare.h" @implementation MainController UIActionSheet *_actionsheet; UIWebView *_webView; - (void)viewDidLoad { [super viewDidLoad]; // 1. アクションシートの生成 _actionsheet = [[UIActionSheet alloc] initWithTitle:@"共有" delegate:self cancelButtonTitle:@"キャンセル" destructiveButtonTitle:nil otherButtonTitles:@"LINE", @"Twitter", @"Facebook", @"Google+", @"メール", @"Safari", @"Evernote", @"URLをコピー", nil]; [_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://google.co.jp"]]]; } #pragma mark - IBAction // 2. アクションシートの表示(シェアボタン押下時) - (IBAction)shareAction:(id)sender { if([[UIDevice currentDevice].model isEqualToString:@"iPad"]){ [_actionsheet showFromBarButtonItem:sender animated:YES]; } else { [_actionsheet showInView:self.view]; } } #pragma mark UIActionSheetDelegate // 3. アクションシートの選択した内容でシェア -(void)actionSheet:(UIActionSheet*)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { NSURL *url = [NSURL URLWithString:[_webView stringByEvaluatingJavaScriptFromString:@"document.URL"]]; NSString *title = [_webView stringByEvaluatingJavaScriptFromString:@"document.title"]; if (0 == buttonIndex) { // LINE NSString *contentURL = [self _escapeURL:[url absoluteString]]; NSString *contentTitleText = [self _escapeURL:title]; NSURL *lineURL = [NSURL URLWithString:[NSString stringWithFormat: @"http://line.naver.jp/R/msg/text/%@?%@", contentURL, contentTitleText]]; [[UIApplication sharedApplication] openURL:lineURL]; } else if (1 == buttonIndex) { // TWITTER SLComposeViewController *postVC = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter]; [postVC setInitialText:title]; [postVC addURL:url]; [self presentViewController:postVC animated:YES completion:nil]; } else if (2 == buttonIndex) { // FACEBOOK SLComposeViewController *postVC = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook]; [postVC setInitialText:title]; [postVC addURL:url]; [self presentViewController:postVC animated:YES completion:nil]; } else if (3 == buttonIndex) { // Google+ id<GPPShareBuilder> shareBuilder = [[GPPShare sharedInstance] shareDialog]; [shareBuilder setPrefillText:title]; [shareBuilder setURLToShare:url]; [shareBuilder open]; } else if (4 == buttonIndex) { // メール NSString *conSbjText = [self _escapeURL:title]; NSString *conBodyText = [self _escapeURL:[url absoluteString]]; NSString *mail = [NSString stringWithFormat:@"mailto:%@?Subject=%@&body=%@", @"", conSbjText, conBodyText]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:mail]]; } else if (5 == buttonIndex) { // Safari [[UIApplication sharedApplication] openURL:url]; } else if (6 == buttonIndex) { // EverNote EvernoteSession *session = [EvernoteSession sharedSession]; [session authenticateWithViewController:self completionHandler:^(NSError *error) { if (!error && session.isAuthenticated){ // We're authenticated! EvernoteUserStore *userStore = [EvernoteUserStore userStore]; [userStore getUserWithSuccess:^(EDAMUser *user) { // success NSLog(@"Authenticated as %@", [user username]); EDAMNote *note = [[EDAMNote alloc] init]; note.title = title; NSString *conDes = [_webView stringByEvaluatingJavaScriptFromString: @"function a(){l=document.getElementsByTagName('meta');for(i=0;i<=l.length;i++){if(l[i].name=='description') return l[i].content}};a();"]; NSString *conImg = [_webView stringByEvaluatingJavaScriptFromString: @"function a(){l=document.getElementsByTagName('meta');for(i=0;i<=l.length;i++){if(l[i].getAttribute('property')=='og:image') return l[i].content}};a();"]; NSString *enNote = [NSString stringWithFormat:@"<p>%@</p><a href='%@'></a>%@<img src='%@' />", conDes, [self _escapeXML:[url absoluteString]], [self _escapeXML:[url absoluteString]], conImg]; note.content = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml2.dtd\"><en-note>%@</en-note>", enNote]; EvernoteNoteStore *store = [[EvernoteNoteStore alloc] initWithSession:[EvernoteSession sharedSession]]; [store createNote:note success:^(EDAMNote *note) { [[[UIAlertView alloc] initWithTitle:@"Evernoteに保存しました。" message:nil delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; } failure:^(NSError *error) { [[[UIAlertView alloc] initWithTitle:@"Evernoteの保存に失敗しました。" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; }]; } failure:^(NSError *error) { [[[UIAlertView alloc] initWithTitle:@"Evernoteの保存に失敗しました。" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; } ]; } }]; } else if (7 == buttonIndex) { // コピー [[UIPasteboard generalPasteboard] setValue:[url absoluteString] forPasteboardType:@"public.text"]; [[[UIAlertView alloc] initWithTitle:@"URLをコピーしました。" message:nil delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; } } #pragma mark - private methods - (NSString *)_escapeURL:(NSString *) url { return (__bridge NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (__bridge CFStringRef)url, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8 ); } - (NSString *)_escapeXML:(NSString *) xml { xml = [xml stringByReplacingOccurrencesOfString:@"&" withString:@"&"]; xml = [xml stringByReplacingOccurrencesOfString:@"\""withString:@"""]; xml = [xml stringByReplacingOccurrencesOfString:@"'"withString:@"'"]; xml = [xml stringByReplacingOccurrencesOfString:@">" withString:@">"]; xml = [xml stringByReplacingOccurrencesOfString:@"<" withString:@"<"]; return xml; }
エンコードについて
Line、メール、EverNoteは、直接URLをシェア出来ません。
シェアするためには、htmlのエスケープが必要になります。
urlのエスケープ(line,mail)→16進表記(文字参照)
xmlのエスケープ(EverNote)→実態参照
文字 | 実体参照 | 10進表記(文字参照) | 16進表記(文字参照) |
---|---|---|---|
"&" | & | & | & |
""" | " | " | " |
"<" | < | < | < |
">" | > | > | > |
" " | |   |   |