Programming/PHP2018. 2. 20. 19:56

PHP에서 Root 권한 계정으로 AD(Active Directory) 비밀번호 변경시 

AD의 이전 비밀번호 재사용 금지 정책 적용


<?php function getPassword($sPassword) { $sPassword = '"' . $sPassword . '"'; $nCount = strlen($sPassword); $sNewPassword = ""; for ($i = 0; $i < $nCount; $i++) { $sNewPassword .= $sPassword[$i] . "\000"; } return $sNewPassword; } $ctrl2012 = array( // LDAP_SERVER_POLICY_HINTS_OID for Windows 2012 and above "oid" => "1.2.840.113556.1.4.2239", "value" => sprintf("%c%c%c%c%c", 48, 3, 2, 1, 1) ); $ctrl2008 = array( // LDAP_SERVER_POLICY_HINTS_DEPRECATED_OID for Windows 2008 R2 SP1 and above "oid" => "1.2.840.113556.1.4.2066", "value" => sprintf("%c%c%c%c%c", 48, 3, 2, 1, 1) ); /****** ** 윈도우 2012이상의 경우 $ctrl2012 사용 ** 윈도우 2008의 경우 $ctrl2008 사용 *******/ if (!ldap_set_option($ldap_connection, LDAP_OPT_SERVER_CONTROLS, array($ctrl2008))) { echo "ERROR: Failed to set server controls"; } $userdata["unicodePwd"] = getPassword('newPassword'); $result = ldap_mod_replace($ldap_connection, $dn , $userdata); if (!$result) { echo "\nErrorNo : ".@ldap_errno($ldap_connection); echo "\nErrorMsg : ".@ldap_error($ldap_connection); }else{ echo 'OK'; } ?>


참고 해외 블로그 : http://laviefrugale.blogspot.kr/2013/01/enforcing-active-directory-password.html

MS  참고 자료 : https://msdn.microsoft.com/en-us/library/cc223320.aspx

Posted by 시니^^
Programming/Etc2017. 7. 14. 19:00


fluentd  인코딩 변환 설정


<source>
from_encoding cp949
encoding utf-8 
</source>


아래와 같이 CP949 에서 UTF-8 변환 실패로 인한 로그 유실 발생

error="\"\\xC1\\xC1\" from CP949 to UTF-8"

iconv 에서의 ignore translit 옵션 처리에 대한 방안 검토


debug 모드로 확인 결과 in_tail.rb 파일 확인

<system>
log_level debug
</system>
2017-07-07 18:57:08 +0900 [warn]: ??? error="\"\\xC1\\xC1\" from CP949 to UTF-8"
2017-07-07 18:57:08 +0900 [debug]: /opt/td-agent/embedded/lib/ruby/gems/2.1.0/gems/fluentd-0.12.35/
lib/fluent/plugin/in_tail.rb:321:in `encode!'


debug모드 에러에서 나온 in_tail.rb 파일 321라인 에 대해서 아래와 같이 수정함 

$vi /opt/td-agent/embedded/lib/ruby/gems/2.1.0/gems/fluentd-0.12.35/lib/fluent/plugin/in_tail.rb line.encode!(@encoding, @from_encoding) => line.encode!(@encoding, @from_encoding, :invalid => :replace, :undef => :replace, :replace => "?")


상황에 따라서 "?" 또는 "" 빈값형태로 대처 한다.

Posted by 시니^^
Programming/node.js2017. 2. 2. 11:23


http://node.green/

? 올리면 예시도 있어서 기능관련 참고하기 좋음 




Posted by 시니^^
Programming/node.js2016. 12. 27. 23:04
NPM : https://github.com/voltrue2/in-app-purchase
var iap = require('in-app-purchase');

iap.config({
    verbose: true,
    //googlePublicKeyStrSandbox: "", //LIVE 먼저 실행하고 에러나면 SandBox 테스트실행함
    googlePublicKeyStrLive: "MII...............",
    googleAccToken: "6/h...........",
    googleRefToken: "9/d...........",
    googleClientID: "99.......kv.7dc.ap...ps.g....nt.com",
    googleClientSecret: "9.....4Qu"
});

iap.setup(function (error) {
    if (error) {
        // error hmm
    }
    var googleReceipt = {
        "data":'{"packageName":"kr.co.test","productId":"kr.co.test.is1","purchaseTime":155435321,
                 "purchaseState":0,"purchaseToken":"oksdf........"}', //{stringified data object}
        "signature": "qSVwa8E9z........."
    };
    iap.validate(iap.GOOGLE, googleReceipt, function (error, response) {
        if (error) {
            console.log(error);
        }
        if (iap.isValidated(response)) {
            var purchaseDataList = iap.getPurchaseData(response);
            console.log(purchaseDataList);
        }
    });
});


API 통신 로직 : node_modules\in-app-purchase\lib\google.js 참고

/** * Function to check subscription status in Google Play * @param {Object} data receipt data * @param {Function} cb callback function */ function checkSubscriptionStatus(data, cb) { data.service = constants.SERVICES.GOOGLE; if (!checkSubscriptionState) { return cb(null, data); } var packageName = data.packageName; var subscriptionID = data.productId; var purchaseToken = data.purchaseToken; var url = 'https://www.googleapis.com/androidpublisher/v2/applications/' + packageName + '/purchases/subscriptions/' + subscriptionID + '/tokens/' + purchaseToken; var state; var getSubInfo = function (next) { verbose.log(NAME, 'Get subscription info from', url); getSubscriptionInfo(url, function (error, response, body) { if (error || 'error' in body) { verbose.log(NAME, 'Failed to get subscription info from', url, error, body); state = constants.VALIDATION.FAILURE; // we must move on to validate() next(); return; }


1. googleClientID / googleClientSecret 생성하기

 1) 설정 => API 액세스 => 기존프로젝트 연결 또는 새프로젝트만들기

 

 2) Google 개발자 콘솔 들어가기 ( https://console.developers.google.com/apis/library?project=xxxx )

3) 사용자 인증 ( 프로젝트 확인 꼭!! ) oauth 생성 > 웹어플리케이션 > 리디렉션 URL ( https://developers.google.com/oauthplayground ) > clientId 및 clientSecret 확인



2. Access 및 Refresh Tokens 얻기

 1) https://developers.google.com/oauthplayground 이동 > 우측설정에서 CilentID와 Client secret 입력


2) Google Play Developer API v2 > 개발자 계정 확인 및 관리 허용 


3) Access Token (googleAccToken) / Refresh Token (googleRefToken)


※ Access Not Configured. Google Play Developer API has not been used in project

   => Google Play Android Developer API 사용으로 설정



※ The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console.

=> 설정 > API 액세스 > 프로젝트가 잘 연결되었는지 OAUTH 클라이언트 ID가 존재하는지 확인 할것



※  errors: [ [Object] ], code: 400, message: 'Invalid Value' } } 

   => 판매되는 상품에 따라서 lib/google.js 에서 url 정보를 purchases/subscriptions => purchases/products 

        변경해야되는 이슈 있음 해당 NPM 보니까 구독(subscriptions) 경우만 지원하므로
        자세한 내용은 아래 API 레퍼런스 참고 해서 수정 하거나 소스 참고해서 새로 짜는 방안도 있다.

        https://developers.google.com/android-publisher/api-ref/purchases/products
        https://developers.google.com/android-publisher/api-ref/purchases/subscriptions

         



Posted by 시니^^
Programming/node.js2016. 12. 27. 20:33

NPM : https://github.com/voltrue2/in-app-purchase


var iap = require('in-app-purchase');
iap.config({
    //verbose: true,
    //googlePublicKeyStrSandbox: "", //LIVE 먼저 실행하고 에러나면 SandBox 테스트실행함
    googlePublicKeyStrLive: "MIIBIj............",
});


iap.setup(function (error) {
    if (error) {
        console.log(error);
    }
    var googleReceipt = {
        "data":'{"packageName":"kr.co.test","productId":"kr.co.test.is1","purchaseTime":155435321,
                 "purchaseState":0,"purchaseToken":"oksdf........"}', //{stringified data object}
        "signature": "qSVwa8E9z........."
    };
    iap.validate(iap.GOOGLE, googleReceipt, function (error, response) {
        if (error) {
            console.log(error);
        }
        console.log(iap.isValidated(response));
    });
});


유니티에서 받는 데이터 중  GoogleReceipt data 값 어떤 걸로 넣는지 찾는데 삽질 2시간 OTL....... 



Hash 검증 로직 : node_modules\in-app-purchase\lib\google.js 참고

function validatePublicKey(receipt, pkey, cb) {
    if (!receipt || !receipt.data) {
        return cb(new Error('missing receipt data'));
    }
    if (!pkey) {
        return cb(new Error('missing public key'));
    }
    if (typeof receipt.data !== 'string') {
        return cb(new Error('receipt.data must be a string'));
    }
    var validater = crypto.createVerify('SHA1');
    var valid;
    validater.update(receipt.data);
    try {
        valid = validater.verify(pkey, receipt.signature, 'base64');
    } catch (error) {
        return cb(error);
    }
    if (valid) {
        // validated successfully
        var data = JSON.parse(receipt.data);
        data.status = constants.VALIDATION.SUCCESS;
        return cb(null, data);
    }
    // failed to validate
    cb(new Error('failed to validate purchase'));
}


GooglePublicKey는 해당 어플리케이션 들어가서 확인 



Posted by 시니^^
Programming/node.js2016. 12. 22. 11:01

https://github.com/expressjs/body-parser


A new body object containing the parsed data is populated on the request object after the middleware (i.e. req.body). This object will contain key-value pairs, where the value can be a string or array (when extended is false), or any type (when extended is true).


POST FORM (Content-Type:application/x-www-form-urlencoded)

$ curl --noproxy '*' -i -XPOST 'http://localhost/post_test' -d 'array[key1][key2]=233'
app.use(bodyParser.urlencoded({ extended: false }));
 // req.body : { 'array[key1][key2]': '233' }
app.use(bodyParser.urlencoded({ extended: true })); 
// req.body : { array: { key1: { key2: '233' } } }


Posted by 시니^^
Programming/node.js2016. 12. 20. 14:28


Node.js 

const crypto = require("crypto");
const algo = 'aes-128-cbc';
const key = "security_key";
const iv = "iv_key";
const value = 'HelloWorld';
const keyBuffer = new Buffer(crypto.createHash('md5').update(key).digest('hex'),"hex");
const ivBuffer = new Buffer(crypto.createHash('md5').update(iv).digest('hex'),"hex");
const textBuffer = new Buffer(value);

let cipher = crypto.createCipheriv(algo, keyBuffer,ivBuffer); let encrypted = cipher.update(textBuffer); let encryptedFinal = cipher.final(); let encryptedText = encrypted.toString('base64') + encryptedFinal.toString('base64'); console.log(encryptedText); //xr046x2iGy4IDUHUm+p7lA==

let decipher = crypto.createDecipheriv(algo, keyBuffer,ivBuffer); decipher.setAutoPadding(true);//padding 처리가 언어별 달라서 확인필요 let decipheredContent = decipher.update(encryptedText,'base64','utf8'); decipheredContent += decipher.final('utf8'); console.log(decipheredContent); //HelloWorld


C# (Unity3D)

출처 : http://wyseburn.tistory.com/entry/AES-CBC-128BIT-PKCS5-%EC%95%94%EB%B3%B5%ED%98%B8%ED%99%94-1

using UnityEngine; using System; using System.Text; using System.IO; using System.Security.Cryptography; using System.Runtime.Remoting.Metadata.W3cXsd2001; public class Crypto { public static readonly string key = MD5Hash("security_key"); public static readonly string iv = MD5Hash("iv_key"); public static string MD5Hash(string str) { MD5 md5 = new MD5CryptoServiceProvider(); byte[] hash = md5.ComputeHash(Encoding.ASCII.GetBytes(str)); StringBuilder stringBuilder = new StringBuilder(); foreach (byte b in hash) { stringBuilder.AppendFormat("{0:x2}", b); } return stringBuilder.ToString(); } private static byte[] GetBytesFromHexString(string strInput) { byte[] bytArOutput = new byte[] { }; if ((!string.IsNullOrEmpty(strInput)) && strInput.Length % 2 == 0) { SoapHexBinary hexBinary = null; hexBinary = SoapHexBinary.Parse(strInput); bytArOutput = hexBinary.Value; } return bytArOutput; } //AES 암호화 public static string AES128Encrypt(string Input) { try { RijndaelManaged aes = new RijndaelManaged(); //aes.KeySize = 256; //AES256으로 사용시 aes.KeySize = 128; //AES128로 사용시 aes.BlockSize = 128; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; aes.Key = GetBytesFromHexString(key); aes.IV = GetBytesFromHexString(iv); var encrypt = aes.CreateEncryptor(aes.Key, aes.IV); byte[] xBuff = null; using (var ms = new MemoryStream()) { using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write)) { byte[] xXml = Encoding.UTF8.GetBytes(Input); cs.Write(xXml, 0, xXml.Length); } xBuff = ms.ToArray(); } string Output = Convert.ToBase64String(xBuff); return Output; } catch (Exception ex) { Debug.LogError(ex.Message); return ex.Message; } } //AES 복호화 public static string AES128Decrypt(string Input) { try { RijndaelManaged aes = new RijndaelManaged(); //aes.KeySize = 256; //AES256으로 사용시 aes.KeySize = 128; //AES128로 사용시 aes.BlockSize = 128; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; aes.Key = GetBytesFromHexString(key); aes.IV = GetBytesFromHexString(iv); var decrypt = aes.CreateDecryptor(); byte[] xBuff = null; using (var ms = new MemoryStream()) { using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write)) { byte[] xXml = Convert.FromBase64String(Input); cs.Write(xXml, 0, xXml.Length); } xBuff = ms.ToArray(); } string Output = Encoding.UTF8.GetString(xBuff); return Output; } catch (Exception ex) { Debug.LogError(ex.Message); return string.Empty; } } }


Posted by 시니^^

elasticsearch 2.x 부터 Root로 보안상문제로 아래와 같이 실행되지 않음


Exception in thread "main" java.lang.RuntimeException: don't run elasticsearch as root.


그리고 shutdown 관련되서 기존 curl로 shutdown가 되지 않음 


https://www.elastic.co/guide/en/elasticsearch/reference/2.1/cluster-nodes-shutdown.html

The _shutdown API has been removed. Instead, setup Elasticsearch to run as a service (see Running as a Service on Linux or Running as a Service on Windows) or use the -p command line option to write the PID to a file.


yum(rpm)으로 설치 할 경우  init.d 스크립트를 만들어줘서 상관 없지만 그냥 소스 파일 가져다가 내가 원하는 위치에 압축풀어서 사용하므로 간단하게 만들었음 현재로서는 내가 원하는 기능만 간략하게~
상황에 따라서 추후 업데이트 예정

#!/bin/sh

DIR=$(echo $(cd $(dirname $0); pwd))

ES_HOME=${DIR}
ES_USER=elasticsearch
ES_HEAP_SIZE=1g
MAX_LOCKED_MEMORY=unlimited
MAX_OPEN_FILES=65535
MAX_MAP_COUNT=200000
PID=${ES_HOME}/elasticsearch.pid

if ! id -u "$ES_USER" >/dev/null 2>&1; then
    groupadd $ES_USER
    useradd -s /sbin/nologin -g $ES_USER $ES_USER
fi

case "$1" in
  start)
    chown -R ${ES_USER}.${ES_USER} ${DIR}

    if [ -n "$MAX_OPEN_FILES" ]; then
        ulimit -n $MAX_OPEN_FILES
    fi

    if [ -n "$MAX_LOCKED_MEMORY" ]; then
        ulimit -l $MAX_LOCKED_MEMORY
    fi

    if [ -n "$MAX_MAP_COUNT" -a -f /proc/sys/vm/max_map_count ]; then
        sysctl -q -w vm.max_map_count=$MAX_MAP_COUNT
    fi

    if [ -e ${PID} ] && [ ps -p `cat ${PID}` > /dev/null 2>&1 ]; then
       echo "Error - elasticsearch Running"
    else
       sudo -u ${ES_USER} ES_HEAP_SIZE="${ES_HEAP_SIZE}" ${ES_HOME}/bin/elasticsearch -d -p ${PID}
    fi

    ;;
  stop)
    if [ -e ${PID} ] ; then
       kill `cat ${PID}`
    fi
    ;;
  *)
    echo $"Usage: $0 {start | stop}"
    exit 1
esac
exit 0


추가로 host 처리 디폴트 localhost(127.0.0.1) 또는 아이피 동시 안되는 문제는 network.host 아래와 같이 하면됨

Allow binding to multiple addresses

https://github.com/elastic/elasticsearch/issues/13592

network.host: 0.0.0.0


Posted by 시니^^

Kibana Plugin 모음


https://github.com/elastic/kibana/wiki/Known-Plugins



Visualizations


Posted by 시니^^

elasticsearch 데이터를 넣으면 데이터를 보고 알아서 필드 타입 대해서 정의 해주는데 

상황에 따라서 정수와 실수에 대한 정의 또는 int와 bigint에 대한 처리를 해줘야 될때가 있다 

그럴 경우 특정 필드에서 대해서 매핑 처리가 가능하다

아래 래퍼런스 사이트 참조

https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html


샘플 데이터 : curl -XPUT localhost:9200/_template/stat --upload-file template.json

{ "order":0, "template":"stat-*", "settings":{ "index.refresh_interval":"5s" }, "mappings":{ "_default_":{ "dynamic_templates":[ { "long_fields":{ //long "match":"long_*", "mapping":{ "type":"long" } } }, { "double_fields":{ //double "match":"double_*", "unmatch":"*_text", //외 처리 가능 레퍼런스보면 정규식형태도 있음 "mapping":{ "type":"double" } } }, { "string_fields":{ //string 드에 대한 처리 "match_mapping_type":"string", "match":"*", "mapping":{ "index":"analyzed", "omit_norms":true, "type":"string", "fields":{ "raw":{ "index":"not_analyzed", "ignore_above":256, "type":"string" } } } } } ], "_all":{ "enabled":true } } }, "aliases":{} }


Posted by 시니^^