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 схем!