iOS SAST
Что такое ios sast: определение, основные принципы, примеры и практические советы. Изучайте фундаментальной защите информации с подробными объяснениями для начинающих специалистов.
iOS SAST - Статический анализ приложений iOS
Что такое iOS SAST?
iOS SAST (Static Application Security Testing) — это процесс анализа безопасности iOS приложений на уровне исходного кода без их выполнения. Включает анализ Swift/Objective-C кода, Info.plist файлов, ресурсов и конфигураций для выявления потенциальных уязвимостей безопасности.
Основные принципы
- Source Code Analysis — анализ исходного кода
- Info.plist Analysis — анализ Info.plist файлов
- Resource Analysis — анализ ресурсов
- Configuration Analysis — анализ конфигураций
- Vulnerability Detection — обнаружение уязвимостей
Архитектура iOS SAST
1. Swift Code Analysis
// Система анализа кода iOS приложений
class iOSCodeAnalyzer {
constructor() {
this.vulnerabilities = new Map();
this.patterns = new Map();
this.rules = new Map();
this.issues = new Map();
}
// Анализ Swift кода
analyzeSwiftCode(codeData) {
const analysis = {
id: this.generateAnalysisId(),
fileType: 'SWIFT',
timeRange: new Date(),
vulnerabilities: [],
issues: [],
patterns: [],
summary: {
totalVulnerabilities: 0,
criticalVulnerabilities: 0,
highVulnerabilities: 0,
mediumVulnerabilities: 0,
lowVulnerabilities: 0
}
};
// Парсинг Swift кода
const parsedCode = this.parseSwiftCode(codeData.content);
analysis.code = parsedCode;
// Анализ уязвимостей
const vulnerabilities = this.findVulnerabilities(parsedCode);
analysis.vulnerabilities = vulnerabilities;
// Анализ проблем
const issues = this.findIssues(parsedCode);
analysis.issues = issues;
// Анализ паттернов
const patterns = this.findPatterns(parsedCode);
analysis.patterns = patterns;
// Расчет сводки
this.calculateCodeSummary(analysis);
return analysis;
}
// Парсинг Swift кода
parseSwiftCode(content) {
const code = {
classes: [],
methods: [],
variables: [],
imports: [],
protocols: []
};
// Упрощенный парсинг Swift кода
const lines = content.split('\n');
let currentClass = null;
let currentMethod = null;
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
// Анализ импортов
if (line.startsWith('import ')) {
code.imports.push({
statement: line,
module: line.replace('import ', ''),
line: i + 1
});
}
// Анализ классов
if (line.includes('class ') && line.includes('{')) {
const className = this.extractClassName(line);
currentClass = {
name: className,
line: i + 1,
methods: [],
variables: []
};
code.classes.push(currentClass);
}
// Анализ протоколов
if (line.includes('protocol ') && line.includes('{')) {
const protocolName = this.extractProtocolName(line);
code.protocols.push({
name: protocolName,
line: i + 1
});
}
// Анализ методов
if (line.includes('func ') && line.includes('(')) {
const method = this.extractMethod(line, i + 1);
if (method) {
currentMethod = method;
if (currentClass) {
currentClass.methods.push(method);
}
code.methods.push(method);
}
}
// Анализ переменных
if (line.includes('var ') || line.includes('let ')) {
const variable = this.extractVariable(line, i + 1);
if (variable) {
if (currentClass) {
currentClass.variables.push(variable);
}
code.variables.push(variable);
}
}
}
return code;
}
// Извлечение имени класса
extractClassName(line) {
const parts = line.split(' ');
const classIndex = parts.indexOf('class');
if (classIndex !== -1 && classIndex + 1 < parts.length) {
return parts[classIndex + 1].split(':')[0].split('{')[0];
}
return 'Unknown';
}
// Извлечение имени протокола
extractProtocolName(line) {
const parts = line.split(' ');
const protocolIndex = parts.indexOf('protocol');
if (protocolIndex !== -1 && protocolIndex + 1 < parts.length) {
return parts[protocolIndex + 1].split(':')[0].split('{')[0];
}
return 'Unknown';
}
// Извлечение метода
extractMethod(line, lineNumber) {
const parts = line.split('(');
if (parts.length >= 2) {
const methodPart = parts[0].trim();
const methodName = methodPart.split(' ').pop();
const returnType = methodPart.replace(methodName, '').trim();
return {
name: methodName,
returnType: returnType,
line: lineNumber,
parameters: this.extractParameters(parts[1])
};
}
return null;
}
// Извлечение параметров
extractParameters(paramString) {
const params = paramString.split(')')[0].split(',');
return params.map(p => p.trim()).filter(p => p.length > 0);
}
// Извлечение переменной
extractVariable(line, lineNumber) {
const parts = line.split('=');
if (parts.length >= 2) {
const varPart = parts[0].trim();
const varName = varPart.split(' ').pop();
const varType = varPart.replace(varName, '').trim();
return {
name: varName,
type: varType,
line: lineNumber,
value: parts[1].trim()
};
}
return null;
}
// Поиск уязвимостей
findVulnerabilities(parsedCode) {
const vulnerabilities = [];
// Уязвимости в методах
const methodVulnerabilities = this.findMethodVulnerabilities(parsedCode.methods);
vulnerabilities.push(...methodVulnerabilities);
// Уязвимости в переменных
const variableVulnerabilities = this.findVariableVulnerabilities(parsedCode.variables);
vulnerabilities.push(...variableVulnerabilities);
// Уязвимости в импортах
const importVulnerabilities = this.findImportVulnerabilities(parsedCode.imports);
vulnerabilities.push(...importVulnerabilities);
return vulnerabilities;
}
// Поиск уязвимостей в методах
findMethodVulnerabilities(methods) {
const vulnerabilities = [];
for (const method of methods) {
// Проверка на небезопасные методы
const unsafeMethods = [
'loadData',
'writeToFile',
'createFileAtPath',
'removeItemAtPath',
'copyItemAtPath'
];
for (const unsafeMethod of unsafeMethods) {
if (method.name.includes(unsafeMethod)) {
vulnerabilities.push({
type: 'UNSAFE_METHOD',
method: method,
severity: 'HIGH',
description: `Unsafe method detected: ${method.name}`
});
}
}
// Проверка на отсутствие проверки разрешений
if (method.name.includes('request') || method.name.includes('access')) {
vulnerabilities.push({
type: 'MISSING_PERMISSION_CHECK',
method: method,
severity: 'MEDIUM',
description: `Missing permission check in method: ${method.name}`
});
}
}
return vulnerabilities;
}
// Поиск уязвимостей в переменных
findVariableVulnerabilities(variables) {
const vulnerabilities = [];
for (const variable of variables) {
// Проверка на небезопасные переменные
if (variable.name.includes('password') || variable.name.includes('secret')) {
vulnerabilities.push({
type: 'SENSITIVE_VARIABLE',
variable: variable,
severity: 'HIGH',
description: `Sensitive variable detected: ${variable.name}`
});
}
// Проверка на хардкод значений
if (variable.value.includes('http://') || variable.value.includes('https://')) {
vulnerabilities.push({
type: 'HARDCODED_URL',
variable: variable,
severity: 'MEDIUM',
description: `Hardcoded URL detected: ${variable.value}`
});
}
}
return vulnerabilities;
}
// Поиск уязвимостей в импортах
findImportVulnerabilities(imports) {
const vulnerabilities = [];
for (const import of imports) {
// Проверка на небезопасные импорты
const unsafeModules = [
'UIKit',
'WebKit',
'AVFoundation',
'CoreLocation',
'Contacts'
];
for (const unsafeModule of unsafeModules) {
if (import.module.includes(unsafeModule)) {
vulnerabilities.push({
type: 'UNSAFE_IMPORT',
import: import,
severity: 'MEDIUM',
description: `Unsafe import detected: ${import.module}`
});
}
}
}
return vulnerabilities;
}
// Генерация ID анализа
generateAnalysisId() {
return 'IOS-CODE-ANALYSIS-' + Date.now() + '-' + Math.random().toString(36).substr(2, 4);
}
}
2. Info.plist Analysis
// Система анализа Info.plist файлов iOS
class iOSInfoPlistAnalyzer {
constructor() {
this.permissions = new Map();
this.schemes = new Map();
this.queries = new Map();
this.entitlements = new Map();
}
// Анализ Info.plist
analyzeInfoPlist(infoPlistData) {
const analysis = {
id: this.generateAnalysisId(),
timeRange: new Date(),
permissions: [],
schemes: [],
queries: [],
entitlements: [],
vulnerabilities: [],
summary: {
totalPermissions: 0,
sensitivePermissions: 0,
totalSchemes: 0,
totalQueries: 0,
totalEntitlements: 0,
},
};
// Парсинг Info.plist
const parsedInfoPlist = this.parseInfoPlist(infoPlistData.content);
analysis.infoPlist = parsedInfoPlist;
// Анализ разрешений
const permissions = this.analyzePermissions(parsedInfoPlist.permissions);
analysis.permissions = permissions;
// Анализ схем
const schemes = this.analyzeSchemes(parsedInfoPlist.schemes);
analysis.schemes = schemes;
// Анализ запросов
const queries = this.analyzeQueries(parsedInfoPlist.queries);
analysis.queries = queries;
// Анализ прав
const entitlements = this.analyzeEntitlements(parsedInfoPlist.entitlements);
analysis.entitlements = entitlements;
// Поиск уязвимостей
const vulnerabilities = this.findInfoPlistVulnerabilities(analysis);
analysis.vulnerabilities = vulnerabilities;
// Расчет сводки
this.calculateInfoPlistSummary(analysis);
return analysis;
}
// Парсинг Info.plist
parseInfoPlist(content) {
const infoPlist = {
permissions: [],
schemes: [],
queries: [],
entitlements: [],
};
// Упрощенный парсинг XML Info.plist
const lines = content.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
// Анализ разрешений
if (line.includes("<key>NS") && line.includes("UsageDescription")) {
const permission = this.extractPermission(line, lines, i);
if (permission) {
infoPlist.permissions.push(permission);
}
}
// Анализ схем
if (line.includes("<key>CFBundleURLSchemes")) {
const scheme = this.extractScheme(line, lines, i);
if (scheme) {
infoPlist.schemes.push(scheme);
}
}
// Анализ запросов
if (line.includes("<key>LSApplicationQueriesSchemes")) {
const query = this.extractQuery(line, lines, i);
if (query) {
infoPlist.queries.push(query);
}
}
// Анализ прав
if (line.includes("<key>com.apple.developer")) {
const entitlement = this.extractEntitlement(line, lines, i);
if (entitlement) {
infoPlist.entitlements.push(entitlement);
}
}
}
return infoPlist;
}
// Извлечение разрешения
extractPermission(line, lines, lineIndex) {
const keyMatch = line.match(/<key>([^<]+)<\/key>/);
if (keyMatch) {
const key = keyMatch[1];
const valueLine = lines[lineIndex + 1];
const valueMatch = valueLine.match(/<string>([^<]+)<\/string>/);
if (valueMatch) {
return {
key: key,
value: valueMatch[1],
type: this.classifyPermission(key),
line: lineIndex + 1,
};
}
}
return null;
}
// Классификация разрешения
classifyPermission(key) {
const sensitivePermissions = [
"NSCameraUsageDescription",
"NSMicrophoneUsageDescription",
"NSLocationWhenInUseUsageDescription",
"NSLocationAlwaysUsageDescription",
"NSContactsUsageDescription",
"NSCalendarsUsageDescription",
"NSRemindersUsageDescription",
"NSPhotoLibraryUsageDescription",
"NSPhotoLibraryAddUsageDescription",
];
if (sensitivePermissions.includes(key)) {
return "SENSITIVE";
} else if (key.includes("UsageDescription")) {
return "NORMAL";
} else {
return "UNKNOWN";
}
}
// Извлечение схемы
extractScheme(line, lines, lineIndex) {
const keyMatch = line.match(/<key>([^<]+)<\/key>/);
if (keyMatch) {
const key = keyMatch[1];
const valueLine = lines[lineIndex + 1];
const valueMatch = valueLine.match(/<string>([^<]+)<\/string>/);
if (valueMatch) {
return {
key: key,
value: valueMatch[1],
type: "URL_SCHEME",
line: lineIndex + 1,
};
}
}
return null;
}
// Извлечение запроса
extractQuery(line, lines, lineIndex) {
const keyMatch = line.match(/<key>([^<]+)<\/key>/);
if (keyMatch) {
const key = keyMatch[1];
const valueLine = lines[lineIndex + 1];
const valueMatch = valueLine.match(/<string>([^<]+)<\/string>/);
if (valueMatch) {
return {
key: key,
value: valueMatch[1],
type: "QUERY_SCHEME",
line: lineIndex + 1,
};
}
}
return null;
}
// Извлечение права
extractEntitlement(line, lines, lineIndex) {
const keyMatch = line.match(/<key>([^<]+)<\/key>/);
if (keyMatch) {
const key = keyMatch[1];
const valueLine = lines[lineIndex + 1];
const valueMatch = valueLine.match(
/<true\/>|<false\/>|<string>([^<]+)<\/string>/
);
if (valueMatch) {
return {
key: key,
value: valueMatch[1] || "true",
type: "ENTITLEMENT",
line: lineIndex + 1,
};
}
}
return null;
}
// Поиск уязвимостей Info.plist
findInfoPlistVulnerabilities(analysis) {
const vulnerabilities = [];
// Проверка на чувствительные разрешения
const sensitivePermissions = analysis.permissions.filter(
(p) => p.type === "SENSITIVE"
);
if (sensitivePermissions.length > 0) {
vulnerabilities.push({
type: "SENSITIVE_PERMISSIONS",
count: sensitivePermissions.length,
severity: "HIGH",
description: `Found ${sensitivePermissions.length} sensitive permissions`,
});
}
// Проверка на отсутствие описаний разрешений
const permissionsWithoutDescription = analysis.permissions.filter(
(p) => !p.value || p.value.trim() === "
);
if (permissionsWithoutDescription.length > 0) {
vulnerabilities.push({
type: "MISSING_PERMISSION_DESCRIPTION",
count: permissionsWithoutDescription.length,
severity: "MEDIUM",
description: `Found ${permissionsWithoutDescription.length} permissions without description`,
});
}
// Проверка на небезопасные схемы
const unsafeSchemes = analysis.schemes.filter(
(s) =>
s.value.includes("http://") ||
s.value.includes("https://") ||
s.value.includes("file://")
);
if (unsafeSchemes.length > 0) {
vulnerabilities.push({
type: "UNSAFE_URL_SCHEMES",
count: unsafeSchemes.length,
severity: "MEDIUM",
description: `Found ${unsafeSchemes.length} unsafe URL schemes`,
});
}
return vulnerabilities;
}
// Генерация ID анализа
generateAnalysisId() {
return (
"IOS-INFOPLIST-ANALYSIS-" +
Date.now() +
"-" +
Math.random().toString(36).substr(2, 4)
);
}
}
Основные компоненты iOS SAST
1. Code Analysis
- Swift/Objective-C Analysis — анализ Swift/Objective-C кода
- Method Analysis — анализ методов
- Variable Analysis — анализ переменных
- Import Analysis — анализ импортов
2. Info.plist Analysis
- Permission Analysis — анализ разрешений
- Scheme Analysis — анализ схем
- Query Analysis — анализ запросов
- Entitlement Analysis — анализ прав
3. Resource Analysis
- String Analysis — анализ строк
- Storyboard Analysis — анализ сторибордов
- Asset Analysis — анализ ресурсов
- Configuration Analysis — анализ конфигураций
Best Practices
1. Analysis Process
- Systematic Approach — системный подход
- Rule-based Analysis — анализ на основе правил
- Pattern Recognition — распознавание паттернов
- Vulnerability Classification — классификация уязвимостей
2. Tools and Techniques
- Static Analysis Tools — инструменты статического анализа
- Code Review — обзор кода
- Automated Scanning — автоматическое сканирование
- Manual Analysis — ручной анализ
3. Security Guidelines
- OWASP Mobile Top 10 — топ-10 мобильных угроз OWASP
- iOS Security Guidelines — руководящие принципы безопасности iOS
- Secure Coding Practices — практики безопасного программирования
- Vulnerability Management — управление уязвимостями
Заключение
iOS SAST — это критически важная область мобильной безопасности, которая требует:
- Глубокого понимания — платформы iOS
- Специализированных навыков — в области анализа кода
- Правильных инструментов — для статического анализа
- Системного подхода — к выявлению уязвимостей
Помните: iOS SAST — это не разовое мероприятие, а постоянный процесс. Регулярно обновляйте инструменты, следите за новыми угрозами и адаптируйте методы анализа.
Совет: Начните с анализа Info.plist, затем переходите к анализу кода. Не забывайте о проверке разрешений и URL схем!